0

With this code, I am able to store strings into namesArray and print them to the screen. How is this possible if namesArray is a pointer to int?

int numNames = getMaxNames(argv[1]);

int* namesArray = malloc(numNames * sizeof(int)); 

int i;
for (i = 0; i< numNames; i++) { 
  scanf("%s", &namesArray[i]);
}
for (i = 0; i< numNames; i++) { 
  printf("%s\n", &namesArray[i]);
}
  • 2
    It's undefined behaviour. So, anything can happen. – P.P Oct 27 '16 at 05:34
  • Both answers so far are horribly incomplete: one is a reasonably accurate explanation of what's happening, the other is an accurate explanation of the fact that the C standard doesn't mandate this behavior. Anyone want to combine them into a single answer that explains both parts? – ruakh Oct 27 '16 at 06:09
  • @ruakh Yes, can do. Need a little time, will beback – Sourav Ghosh Oct 27 '16 at 07:08

2 Answers2

2

You invoke undefined behavior by writing

 scanf("%s", &namesArray[i]);

where, &namesArray[i] is of type int *.

Quoting C11, chapter §7.21.6.2, fscanf() (emphasis mine)

s Matches a sequence of non-white-space characters.286) If no l length modifier is present, the corresponding argument shall be a pointer to the initial element of a character array large enough to accept the sequence and a terminating null character, which will be added automatically.

and,

[...] Unless assignment suppression was indicated by a *, the result of the conversion is placed in the object pointed to by the first argument following the format argument that has not already received a conversion result. If this object does not have an appropriate type, or if the result of the conversion cannot be represented in the object, the behavior is undefined.

That said, you really ought to check the success of malloc() before using the returned pointer.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
0

C doesn't care what the pointer is to. If sizeof(char) is 1, and sizeof(int) is 4, then malloc(sizeof(int)) is the same as malloc(sizeof(char)*4). Note malloc returns a void * anyway. Since C doesn't really care what the pointer is to, you can always just cast buffers and it won't complain. Also note, you can do this cowboy style casting between types and C doesn't care, but it isn't really good form, and many compilers will complain about it.

so consider this code:

char* str = (char*)malloc(sizeof(char)*4);
int* intArr = (int*)malloc(sizeof(int));
//it is perfectly legal to do this:
char* intStr = (char*)intArr;
//or
printf("int char %c\n", ((char*)intArry)[2]);
//or
printf("int char %c\n, intStr[2]);
//or
printf("int char %c\n, intStr+2);
//or copy intArr into str
for (int i = 0; i < 4; i++) {str[i] = ((char*)intArr)[i];}

With that said, you should actually do this kind of stuff unless you really know how pointers work and what you are doing. Make sure you are doing it for the right reasons. Because it seems to work is not a right reason. :)

So for your code, you could "fix" it like this, but this code is still "very bad." scanf is not safe. It can easily overflow your namesArray buffer.

int numNames = getMaxNames(argv[1]);
char* namesArray = (char*)malloc(numNames * sizeof(int));  
int i = 0;

for (i = 0; i< numNames; i++) { 
  scanf("%s", namesArray);
}
for (i = 0; i< numNames; i++) { 
  printf("%s\n", namesArray);
}

` OR

int numNames = getMaxNames(argv[1]);
int* namesArray = (int*)malloc(numNames * sizeof(int));  
int i = 0;

for (i = 0; i< numNames; i++) { 
  scanf("%s", (char*)namesArray);
}
for (i = 0; i< numNames; i++) { 
  printf("%s\n", (char*)namesArray);
}
sam msft
  • 537
  • 6
  • 17
  • 1
    [Please see this discussion on why not to cast the return value of `malloc()` and family in `C`.](http://stackoverflow.com/q/605845/2173917). – Sourav Ghosh Oct 27 '16 at 06:04
  • Clearly I disagree with a popular opinion in that thread. For me personally, I prefer to always cast when doing assignments between different types. The compiler I will spit out a warning when you do, which I have the flag set to treat warnings as an errors. You can also ignore return codes. C doesn't care, but I enable that warning too, which gets covered to an error. I cast the function to void to be explicit that I am intentionally ignoring the return. C doesn't care, but it is the style I use., – sam msft Oct 27 '16 at 06:13
  • 1
    @sammsft The compiler will _not_ spit out a warning when you convert from `void*` to another pointer type. Sounds like you are trying to compile C code with a C++ compiler. If so - bad idea. – Lundin Oct 27 '16 at 06:43
  • What if the type of `str` changes? Then everywhere you assign to it you also have to change the cast... As students we're taught this kind of repetitive code (the casting) is unnecessary, even harmful... You want to reduce maintainability, and increase your future arthritic pain? So be it. Compile C code using a C compiler (and no typecasts for this), and C++ code using a C++ compiler (and no `malloc`). You can't have the best of both worlds; if you try, you'll only end up with the worst. – autistic Oct 27 '16 at 12:15
  • @Lundin I think you are being a little pedantic. When you say "The compiler," there are many C/C++ compilers that have differing behaviors. Also some compilers have custom options of what kind of things you want to treat as warnings and errors. Your compiler may not care with its configuration and implementation, but that does not mean that all C/C++ compilers will not care. In my case, our build environment has many standardized settings for the C/C++ compiler baked in to avoid bad C patterns. It also run many static analysis tools automatically each time a object file is compiled. – sam msft Oct 27 '16 at 18:43
  • The ANSI C book is full of unsafe failure prone hacker C code. Even though you *can* code that way, you should not. The world was a lot simpler back then. – sam msft Oct 27 '16 at 18:54
  • @sammsft The thing is, there is no rationale for why an explicit cast would improve the code. It may just as well break the code - probably not so much because of the C90 implicit int bug discussed in the linked posts - as for more complex expression like `int (*arr2d)[x][y] = malloc( sizeof(int[x][y]) );`. An explicit cast would clutter up the code and make it harder to read: `int (*arr2d)[x][y] = (int(*)[x][y])malloc( sizeof(int[x][y]) );`. And you gain nothing from the cast except perhaps a false sense of type safety. Though this code is only type safe as long as you type it correctly... – Lundin Oct 28 '16 at 06:32
  • I am not sure what you mean that an explicit cast may well break the code. I am not sure why that would be the case. It is clear that you feel very strong about not doing it, so by all means, do not do it in your code. As to what style is more readable, I think that is a personal preference. – sam msft Nov 05 '16 at 22:43
  • I work in a code base with over a billion C/C++ source code files. There are *many* coding styles, many memory allocators, etc., each project has its own set of compiler flags, style, and so on. For example, much of the kernel code is standard C style code. There is also COM style code, ATL, RPC code, STL style code, Cx, C++11 is popular these days in new features, WRL, WIL, bla bla bla. Some use exceptions, most don't. The main rule is to consistently match the style and patterns of the code base you are working in...and don't write bugs. – sam msft Nov 05 '16 at 23:00