Connecting Tech Pros Worldwide Help | Site Map

Requiring & operator for one member of struct, but not for another.

Markus's Avatar
Moderator
 
Join Date: Jun 2007
Location: York, England, with wolves.
Posts: 4,936
#1: Oct 12 '09
Can you explain why line 31 does not require the address-of operator, while 33 does? I don't quite understand it.

Expand|Select|Wrap|Line Numbers
  1. #include <stdio.h>
  2.  
  3. struct computer {
  4.     float cost;
  5.     int   year;
  6.     int   cpu_speed;
  7.     char  cpu_type[16];
  8. };
  9.  
  10. typedef struct computer SC;
  11.  
  12. void DataReceive(SC *ptr_s);
  13.  
  14. int main(int argc, char** argv)
  15. {
  16.     SC model;
  17.  
  18.     DataReceive(&model);
  19.     printf("Here is what you entered: \n");
  20.     //printf("Year: %d\n", model.year);
  21.     //printf("Cost: %6.2f\n", model.cost);
  22.     printf("CPU Type: %s\n", model.cpu_type);
  23.     printf("CPU Speed: %d MHz\n", model.cpu_speed);
  24.  
  25.     return 0;
  26. }
  27.  
  28. void DataReceive(SC *ptr_s) 
  29. {
  30.     printf("CPU Type: ");
  31.         scanf("%s", ptr_s->cpu_type);
  32.     printf("CPU Speed: ");
  33.         scanf("%d", &ptr_s->cpu_speed);
  34. }
  35.  
Thanks,
Mark.
Expert
 
Join Date: Mar 2008
Location: Naperville, Illinois U.S.
Posts: 828
#2: Oct 12 '09

re: Requiring & operator for one member of struct, but not for another.


Quote:

Originally Posted by Markus View Post

Can you explain why line 10 does not require the address-of operator, while 12 does?
Line numbers changed to match redacted code listing. [donbock]

Expand|Select|Wrap|Line Numbers
  1. struct computer {
  2.     int   cpu_speed;
  3.     char  cpu_type[16];
  4. };
  5. typedef struct computer SC;
  6.  
  7. void DataReceive(SC *ptr_s) 
  8. {
  9.     printf("CPU Type: ");
  10.     scanf("%s", ptr_s->cpu_type);
  11.     printf("CPU Speed: ");
  12.     scanf("%d", &ptr_s->cpu_speed);
  13. }

From scanf documentation:
  • Conversion specifier s: the corresponding argument shall be a pointer to the initial character of an array large enough to accept the sequence and a terminating null character, which will be added automatically.
  • Conversion specifier d: the corresponding argument shall be a pointer to integer.
The argument for the %s conversion specifier is a char array. An array specifier is almost always equivalent to a pointer to the first element of the array; hence ptr_s->cpu_type is the same as &ptr_s->cpu_type[0] so it is a pointer to char as expected by scanf.

The argument for the %d conversion specifier is the address of an int; hence it is a pointer to int as expected by scanf.

By the way, you might prefer the following to preclude overflowing the cpu_type array:
Expand|Select|Wrap|Line Numbers
  1.     scanf("%15s", ptr_s->cpu_type);
Markus's Avatar
Moderator
 
Join Date: Jun 2007
Location: York, England, with wolves.
Posts: 4,936
#3: Oct 12 '09

re: Requiring & operator for one member of struct, but not for another.


Thank you very much, Donbock. That helps a whole bunch.

Quick q - what happens if the char array does overflow? I have experimented, but I didn't understand the output: stack smashing detected.

Thanks,
Mark.
Banfa's Avatar
AdministratorVoR
 
Join Date: Feb 2006
Location: South West UK
Posts: 6,158
#4: Oct 12 '09

re: Requiring & operator for one member of struct, but not for another.


If you write outside the bounds of the array you get undefined behaviour.

Undefined behaviour is bad because anything can happen. The worst thing that can happen is that the program works as expected right up until the moment it is critical that it works and then it doesn't work in some catastrophic way.

If you are lucky undefined behaviour produces some result that you can immediately identify as wrong. However it is often hard to diagnose the exact cause of undefined behaviour when it happens.

stack smashing specifically refers to your compiler detecting that you have written to a portion of the stack that was not being used for the array. This is bad because the stack contains many critical pieces of information like the return address to jump to once the function has finished. Overwriting this would be a critical error (I hope you can see).
Expert
 
Join Date: Mar 2008
Location: Naperville, Illinois U.S.
Posts: 828
#5: Oct 12 '09

re: Requiring & operator for one member of struct, but not for another.


Suppose the operator responds to the 'CPU Type" prompt by entering 63 characters plus newline. The scanf function will write the 63 characters plus terminating null (64 characters in all) to the array. However, the size of the array is only 16 characters. This means that the next 48 bytes of memory are unintentionally corrupted.

Those 48 bytes might be other program variables; they might be cpu register values saved on the stack; they might be a function return address saved on the stack; they might be nonexistent memory; they might be memory-mapped I/O devices, or they might be executable code within your program.

The consequences of corrupting those 48 bytes are truly unpredictable. That's why the C Standard calls this "undefined behavior".

When I said earlier that these bytes are unintentionally corrupted; I meant from the programmer's point-of-view. It might indeed have been the operator's intention to overflow your buffer. This is one of the techniques used by black hats to take over a computer.

You may have heard that Standard C function gets is deprecated. That's because unlike scanf where you can specify the maximum field length, gets provides no way for the program to protect itself from an operator who deliberately or inadvertently overflows the input buffer.
Reply