0

I am forced to use memset and drand48() to set a random number (2 - 7) of random characters that are lower case letters ('a' to 'z'). My code returns non ASCII characters and I am not sure why.

struct Record {
    int seqnum;
    float threat;
    unsigned int addrs[2];
    unsigned short int ports[2];
    char dns_name[NUMLTRS];
};

My code is in a for loop:

memset(rec_ptr[i].dns_name, (char)((122 * drand48()) + 97), 
((sizeof(char) * 7) * drand48()) + (sizeof(char) * 2));
jch3
  • 1
  • 1
  • 2
    ASCII's range is only `0` - `127`. Unless `drand48()` `return`s `0` in `(char)((122 * drand48()) + 97)`, it won't fit. – Fiddling Bits Nov 21 '18 at 19:20
  • Hint: Use `'a'` (single quotes) to represent a `char` that's the ASCII value of "a". There's no need to guess. 122 is a `'z'`, so you're at the end of that range already, and 122 + 97 is way out of the range of a-z. – tadman Nov 21 '18 at 19:22
  • 1
    Why are you forced to use `memset`? Doing `dns_name[i] = ...` seems more logical if you have to bang out multiple characters. – tadman Nov 21 '18 at 19:23
  • 1
    `sizeof char` is `1` per definition. Always. And `1` is neutral for multiplication, so drop it. `lrand48()` would be better fitting since you want integers. – Swordfish Nov 21 '18 at 19:24
  • If I'm correct, drand48() returns a double between 0 and 1.0. So how would I get it to return a value between 97 and 122? Is it even possible? – jch3 Nov 21 '18 at 19:31
  • @jch3 `(rand() % 26) + 97` should work. Check my math. – Fiddling Bits Nov 21 '18 at 19:37
  • 2
    . o O ( everytime one uses [magic numbers](https://stackoverflow.com/questions/47882/what-is-a-magic-number-and-why-is-it-bad), a kitty dies :( :( ) – Swordfish Nov 21 '18 at 19:40
  • 1
    The problem with `memset` is that it's a very good way of setting multiple bytes to the same value, but a very poor way of setting many bytes to different values. In this case I think it would be far simpler to use one array assignment for each letter. – Tim Randall Nov 21 '18 at 19:59
  • 1
    @Swordfish: 1 is a magic number too, and concealing it by removing `sizeof(char)` means somebody may miss it when converting code that handles characters to code that handles wide characters with `wchar_t`. – Eric Postpischil Nov 21 '18 at 20:33
  • @EricPostpischil The way it stands now (`sizeof(char)`) is useless and `memset()` wont work well for chars wider than 1, so ... – Swordfish Nov 21 '18 at 20:43
  • @Swordfish: Using `sizeof(char)` is no more useless than changing `97` to `'a'`. It has the same result in the current compiler but signals meaning to future readers. The fact that the future editor will also have to change `memset` to something else is a separate additional issue. – Eric Postpischil Nov 21 '18 at 20:52
  • 1
    @EricPostpischil: `sizeof(char)` is the loneliest number... – Bob Jarvis - Слава Україні Nov 21 '18 at 23:35

1 Answers1

1

My code returns non ASCII characters and I am not sure why.

Wrong scale used to generate lower case letters.

(122 * drand48()) + 97 converted to an integer type can readily make 122 different values. [97...218]. This is outside the ASCII range of [0...127].


How do I set a random number of random lowercase character ...

drand48() provides a random value [0...1.0). Scale by 26 and truncate to get 26 different indexes.

int index = (int) (drand48()*26);  // 0...25

Pedantic code would be concerned about the few random values that may round the product to 26.0

if (index >= 26) index = 26 - 1;
int az = index + 'a';
// or look up in a table if non-ASCII encoding might be used
//        12345678901234567890123456
int az = "abcdefghijklmnopqrstuvwxyz"[index];

Selecting a random length would use the same thing, but with NUMLTRS instead of 26.

int length = (int) (drand48()*NUMLTRS); 
if (index >= NUMLTRS) index = NUMLTRS -1;

... to a Struct member using memset in C

It is unclear if dns_name[] should be all the same, or generally different letters.

struct Record foo;
if (all_same) [
  memset(foo.dns_name, az, length);
} else {
  for (int i = 0; i < length; i++) {
    int index = (int) (drand48()*26);  // 0...25
    if (index >= 26) index = 26 -1;
    int az = index + 'a';
    foo.dns_name[i] = az;  // Does not make sense to use memset() here
  }
}

Lastly, if dns_name[] is meant to be a string for ease of later use, declare with a +1 size

dns_name[NUMLTRS + 1];

// above code

foo.dns_name[NUMLTRS] = '\0'; // append null character
printf("dna_name <%s>\n", foo.dns_name);
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Can `(int)(0.9999...)` really get rounded up to `1`? "When a finite value of real floating type is converted to an integer type other than _Bool, the fractional part is discarded (i.e., the value is truncated toward zero)" ([6.3.1.4 of ISO/IEC 9899:1999)](http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf) – Jongware Nov 21 '18 at 23:04
  • 1
    @usr2564301 The issue is not about `(int)(0.9999...)`, but could `0.9999... * 26` under _any rounding mode_ and any `FLT_EVAL_MODE` result in 26.0? True, mathematically `0.9999... * 26` is always less than 26, but this is FP math. A deep analysis may show 26.0 is not possible, but not having done that, a check is prudent. – chux - Reinstate Monica Nov 21 '18 at 23:08