-1

It is from the book "C Primer Plus" 4.2.2 String I'm using MAC Xcode to try the example code myself.

Here is the code:

#include <stdio.h>
#define PRAISE "You are an extraordinary being."
int main(void)
{
    char name[1];

    printf("What\'s your name?\n");
    scanf("%s", name);
    printf("Hello, %s. %s\n", name, PRAISE);

    return 0;
}

when I input "aaaaaaaaaaaaa", intotal 13 letters, the screen shows:

libdyld.dylib`dyld3::MachOFile::forEachLoadCommand:
0x7fff697aa8a8 <+0>:   pushq  %rbp
0x7fff697aa8a9 <+1>:   movq   %rsp, %rbp
0x7fff697aa8ac <+4>:   pushq  %r15
0x7fff697aa8ae <+6>:   pushq  %r14
0x7fff697aa8b0 <+8>:   pushq  %r13
0x7fff697aa8b2 <+10>:  pushq  %r12
0x7fff697aa8b4 <+12>:  pushq  %rbx
0x7fff697aa8b5 <+13>:  subq   $0x28, %rsp
0x7fff697aa8b9 <+17>:  movb   $0x0, -0x29(%rbp)
0x7fff697aa8bd <+21>:  movq   %rdx, %r12
0x7fff697aa8c0 <+24>:  movq   %rsi, %rbx
0x7fff697aa8c3 <+27>:  movq   %rdi, %r15
->  0x7fff697aa8c6 <+30>:  movl   (%r15), %edx
0x7fff697aa8c9 <+33>:  cmpl   $0xfeedface, %edx         ; 
imm = 0xFEEDFACE 
0x7fff697aa8cf <+39>:  je     0x7fff697aa8e3            ; 
<+59>
0x7fff697aa8d1 <+41>:  cmpl   $0xfeedfacf, %edx         ; 
imm = 0xFEEDFACF 
Diagnostics::error(char const*, ...)
0x7fff697aa9a2 <+250>: addq   $0x10, %rsp
0x7fff697aa9a6 <+254>: addq   $0x28, %rsp
0x7fff697aa9aa <+258>: popq   %rbx
0x7fff697aa9ab <+259>: popq   %r12
0x7fff697aa9ad <+261>: popq   %r13
0x7fff697aa9af <+263>: popq   %r14
0x7fff697aa9b1 <+265>: popq   %r15
0x7fff697aa9b3 <+267>: popq   %rbp
0x7fff697aa9b4 <+268>: retq   

I omitted some codes above for briefty.

If I input more letters, say 20 "a", It returns: error: memory read failed for 0x7fff00616000

If I change the fifth line into: char name[2];

and input 14 characters, it returns the same result.

similarly, If I change the fifth line into: char name[3];

and input 15 characters, it returns the same result.

I know it has something to do with the size of the aaray, however, I have no idea how the number in the bracket is connected to the corresponding number of input characters. Could anyone explain it with details?

lydia
  • 61
  • 5
  • 2
    *"I know it has something to do with the size of the aaray, however, I have no idea how the number in the bracket is connected to the corresponding number of input characters."* It has to be big enough to fit your input, otherwise you get undefined behavior, which can manifest as the "weird things" that you observe. If your array isn't big enough, the program will likely try anyway, which can corrupt the memory somewhere else. – Blaze Jun 18 '19 at 15:34
  • 7
    If you have a bottle that can hold 1 liter of water but you try to pour 15 liters of water into it, the result will be a mess. – Nikos C. Jun 18 '19 at 15:40
  • To prevent that mess, we safeguard the array by limiting what can be entered. Here `scanf("%1s", name);` with the added `1` being the number of bytes storage available in the array `char name[1];` – Weather Vane Jun 18 '19 at 15:42
  • Unfortunately you've found a bug in that book. (Unfortunately that book contains many bugs. If you have a better book available to you, just throw *C Primer Plus* away and don't waste any more time on it.) – Steve Summit Jun 18 '19 at 15:44
  • @WeatherVane But of course, that's a pretty useless fix! Lydia, I would suggest changing the array to `char name[31]`, then using `scanf("%30s", name);`. – Steve Summit Jun 18 '19 at 15:46
  • @SteveSummit yes one less, thanks. – Weather Vane Jun 18 '19 at 15:47
  • "one less" – it is because strings are zero-terminated, i. e. there's always a trailing zero character (if you write `"hello"` as character array, which it actually is, it will look like `{ 'h', 'e', 'l', 'l', 'o', 0 }` – in consequence your `name[1]` would be able only to store the empty string... – Aconcagua Jun 18 '19 at 16:00

2 Answers2

3

char name[1]; is only large enough to store a single character. If you try to store more than 1 character to it, those remaining characters are written to memory following the end of the array, which can potentially overwrite "important" data, leading to a crash (or worse, corrupting data that's used elsewhere).

A string is a sequence of character values including a 0-valued terminator. If you want to store an N-character string, you need to allocate an array that's at least N+1 elements wide. So, if you want to store up to a 13-character string in name, then you need to declare it as

char name[14]; // +1 to hold the string terminator

It's still possible to write more characters to the string than it's sized to hold, so you need to be careful on the input side. scanf allows you to specify a maximum number of characters to read:

scanf( "%13s", name ); // at most 13 characters read into name

Unfortunately, that field width cannot be specified with an argument (the way it can in printf). Another option is to not use scanf at all, and use fgets instead:

fgets( name, sizeof name, stdin );

fgets will read no more than sizeof name - 1 characters from stdin and write them to name, and then add the 0 terminator to the end of name. sizeof name evaluates to the number of bytes in name.

John Bode
  • 119,563
  • 19
  • 122
  • 198
  • `#define ARRAY_BASE_LENGTH 7 #define ARRAY_LENGTH (ARRAY_BASE_LENGTH + 1) array[ARRAY_LENGTH]; scanf("%" #ARRAY_BASE_LENGTH "s", array);` would be a little trick to avoid repeating magic numbers. Not really nice, but perhaps helpful if `scanf`is used to read several different values at once or with surrounding context (@lydia: always check the number of values actually read, i. e. the return value of scanf, if doing so!). – Aconcagua Jun 18 '19 at 16:06
0

weird things happening on your screen is because you have specified the limit of array created '1' (i.e., name[1]) and so the input will be maximum of only one character. You should edit the limit of array to 15 or more (i.e., char name[15]), and so user input will increased to fifteen and so will able to input characters upto 15. Hope this helps!