Thread: the null terminating character

  1. #1
    Registered User
    Join Date
    Feb 2016
    Posts
    20

    the null terminating character

    I don't understand why this is working !

    I'm a bit confused because there's no room free in my "str" when the size of an input is greater than 4 !

    my code:

    Code:
    #include <stdio.h>
    
    int main( int argc, char *argv[] )  {
        
        printf("Start with 1 char: ");
        
        char c = getchar(); // reads 1 char (...) => buffer.
        char str[5]; // t e s t e r ==> [t][e][s][t][\0][e][r][\n] ([e][r][\n]) in buffer !
        
        putchar(c); 
        printf("%c\n", putchar(c)); // print twice: 3 in total
        
        //while (c = getchar() != '\n');
        
        while (1) {
            printf("I. String: ");
            scanf("%s", str);
            printf("O. String: %s\n", str);     
            putchar('\n');
        }
        
        return 0;
    }
    I tested it with gcc windows and linux ! It works and no warnings ..

  2. #2
    misoturbutc Hodor's Avatar
    Join Date
    Nov 2013
    Posts
    1,795
    Well, I guess the simple answer is that it doesn't work

    The compiler is of course not going to give a warning... it has no idea what input the program will receive after it's compiled.

    You're just getting "lucky" that nothing untoward is happening (it's undefined behaviour)... your program is (if it gets a string > 4 characters) causing a buffer overflow, but it just happens to have no side effects that you can observe.

    Code:
    #include <stdio.h>
    
    int main(void)  {
    
        int i;
        char str[5];
    
        for (i = 0; i < 65535; i++) {
            printf("%d\n", i);
            str[i] = 'A';
        }
    
        return 0;
    }
    Edit, example run of the above program using valgrind (your results may vary; it's undefined behaviour after all)

    Code:
    <snipped>
    4878
    4879
    4880
    ==2082== Invalid write of size 1
    ==2082==    at 0x400652: main (yvan.c:10)
    ==2082==  Address 0xfff001000 is not stack'd, malloc'd or (recently) free'd
    ==2082== 
    ==2082== 
    ==2082== Process terminating with default action of signal 11 (SIGSEGV)
    ==2082==  Access not within mapped region at address 0xFFF001000
    ==2082==    at 0x400652: main (yvan.c:10)
    ==2082==  If you believe this happened as a result of a stack
    ==2082==  overflow in your program's main thread (unlikely but
    ==2082==  possible), you can try to increase the size of the
    ==2082==  main thread stack using the --main-stacksize= flag.
    ==2082==  The main thread stack size used in this run was 8388608.
    Last edited by Hodor; 03-05-2016 at 10:28 PM.

  3. #3
    Registered User
    Join Date
    Feb 2016
    Posts
    20
    now I see why some authors say you should use valgrind

    now, I tried something, on linux and windows.

    Code:
    #include <stdio.h>
    #include <string.h>
     
    int main( int argc, char *argv[] )  {
    
    
        char str[5]; // t e s t e r ==> [t][e][s][t][\0][e][r][\n] ([e][r][\n]) in buffer !
        
        strcpy(str, "tester");
        
        printf("String: %s\n", str);  
        
        return 0;
    }
    I got this !

    Quote Originally Posted by linux
    yvan@dbn:~/ict/clang$ gcc -o nullchar nullchar.c
    yvan@dbn:~/ict/clang$ ./nullchar
    String: tester
    *** stack smashing detected ***: ./nullchar terminated
    Aborted (core dumped)

    yvan@dbn:~/ict/clang$
    Quote Originally Posted by windows
    C:\Users\yvan\gt\ict\c lang>gcc -o nullchar.exe nullchar.c

    C:\Users\yvan\gt\ict\c lang>nullchar.exe
    String: tester

    C:\Users\yvan\gt\ict\c lang>

  4. #4
    Registered User
    Join Date
    Jun 2011
    Posts
    4,508
    That code also exhibits undefined behavior, which means anything can happen - including the program appearing to work even though it is broken.

  5. #5
    Registered User
    Join Date
    Feb 2016
    Posts
    20
    thank you
    and yet, they say c is high level, it looks too low for me.
    any good book or www on "undefined behavior" when doing c?

    kr

  6. #6
    Registered User
    Join Date
    Jun 2015
    Posts
    1,643
    Quote Originally Posted by yvan View Post
    thank you
    and yet, they say c is high level, it looks too low for me.
    any good book or www on "undefined behavior" when doing c?
    "They" meant high-level compared to assembly language.
    Compared to other languages it's pretty much as low-level as you can get.

  7. #7
    Registered User
    Join Date
    Jun 2011
    Posts
    4,508
    Quote Originally Posted by yvan View Post
    any good book or www on "undefined behavior" when doing c?
    You could always peruse the latest standard, or the latest draft for free (see Annex J).

  8. #8
    Registered User
    Join Date
    Dec 2015
    Posts
    68
    C is bare to the metal language (a good thing)
    Everything is compiled in to machine code and nothing is evaluated at run time.
    So things like strlen() is just sub-routine where it check byte by byte in machine-code
    probably using indirect-addressing e.g pointer, until it sees a byte that has zero value

  9. #9
    Registered User
    Join Date
    Feb 2016
    Posts
    20
    strlen() ! that is a good one.

    I have another program

    Code:
            //an array of x cities whereby each city of y chars
        int x = 3;
        int y = 40;    
        char cities[x][y];
    where I use gets() to read some strings with white spaces if available instead of scanf() and sscanf().

    if I want to print the cities I'd get those weird characters so I have:

    Code:
            for (i = 0; i < x; i++) {
            for (j = 0; j < y; j++) {
                cities[i][j] = '\0';
            }
        }
    or use strlen() after gets() to make '\0' char.

    Code:
             for (i = 0; i < x; i++) {
            printf("Set city %d: ", i+1);
            gets(cities[i]);
            int len = strlen(cities[i]);
            cities[i][len+1] = '\0';
        }
    that doesn't work !

    the source code:

    Code:
    #include <stdio.h>
    #include <string.h>
    
    
    int main (void) {
    	
    	//an array of x cities whereby each city of y chars
    	int x = 3;
    	int y = 40;	
    	char cities[x][y];
    	
    	int i, j; // loop counters
    	
    	/* for (i = 0; i < x; i++) {
    		for (j = 0; j < y; j++) {
    			cities[i][j] = '\0';
    		}
    	} */
    	
    	for (i = 0; i < x; i++) {
    		printf("Set city %d: ", i+1);
    		gets(cities[i]);
    		int len = strlen(cities[i]);
    		cities[i][len+1] = '\0';
    	}
    	
    	putchar('\n');
    	
    	for (i = 0; i < x; i++) {
    		for (j = 0; j < y; j++) {
    			if (cities[i][j] != '\0') {
    				printf("[ %c ]", cities[i][j]);
    			}
    		}
    		putchar('\n');
    	}
    	
    	return 0;
    }
    Last edited by yvan; 03-07-2016 at 05:30 PM.

  10. #10
    Registered User
    Join Date
    Dec 2015
    Posts
    68
    >char cities[x][y];

    I think I already said C is NOT a run-time language
    allocation of arrays has to be done at compile (maybe at runtime with memalloc etc)
    Code:
    enum {x = 3, y = 40};         // at compile-time define
    char cities[x][y];

  11. #11
    Registered User
    Join Date
    Jun 2011
    Posts
    4,508
    Quote Originally Posted by yvan
    where I use gets() to read some strings with white spaces if available instead of scanf() and sscanf().
    Don't use "gets()"! FAQ > Why gets() is bad / Buffer Overflows - Cprogramming.com
    Use "fgets()" instead: FAQ > Get a line of text from the user/keyboard (C) - Cprogramming.com

    Quote Originally Posted by yvan
    if I want to print the cities I'd get those weird characters so I have:
    You do not need to manually insert the null character when reading a string using functions like scanf, fgets, etc. They will include it for you.

    Look at the logic you're using to print the strings:

    Code:
    for (i = 0; i < x; i++) {
        for (j = 0; j < y; j++) {
            if (cities[i][j] != '\0') {
                printf("[ %c ]", cities[i][j]);
            }
        }
        putchar('\n');
    }
    You're printing character by character. The "if()" does not print a character if the '\0' is found ... but you continue cycling after that, so whatever happens to be in the array after the '\0' is printed.

    One solution would be to initialize the array to zero. Unfortunately, if you're using a variable-length array, then you cannot initialize it. If you use constants to declare your array, you would just do:

    Code:
    #define WORD    3
    #define LETTER 40
    
    //...
    
    char cities[WORD][LETTER] = { {0} };
    Another solution would be to just stop printing a word when the null character is found:

    Code:
    for (i = 0; i < X; i++) {
        for (j = 0; j < y && cities[i][j] != '\0'; j++) {
    This also means you would not require the "if()" statement in the loop, since the inner loop would just end at the '\0'.

    A third solution is to just print them as strings:

    Code:
    for (i = 0; i < x; i++) {
        puts(cities[i]);  // "puts()" automatically prints a newline after the string
    }
    I would suggest the third solution ... unless there's a particular reason you want to output one character at a time, then I would suggest the second solution.

    I would also recommend you avoid using variable-length arrays if they are not strictly needed. Fixed-sized arrays would suit your purposes just fine.

    Quote Originally Posted by tonyp12 View Post
    >char cities[x][y];

    I think I already said C is NOT a run-time language
    allocation of arrays has to be done at compile (maybe at runtime with memalloc etc)
    Starting from C99, variable-length arrays have been supported. However, I personally avoid using them myself, and usually encourage new programmers to stick with fixed-sized arrays.

  12. #12
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,407
    Quote Originally Posted by tonyp12
    Everything is compiled in to machine code and nothing is evaluated at run time.
    Not quite:
    • What you probably meant to say is that "nothing is interpreted at run time".
    • Runtime interpreters for C can and do exist.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  13. #13
    Registered User
    Join Date
    Feb 2016
    Posts
    20
    dear Matticus,
    thank you for the answers and explanation but I get this feeling you are addressing me as if I am a developer of some real system/software, which is not the case because I am learning, trying to discover and understand some C.

    so, I have some problems when I hear or read people saying don't use x() don't use y() while these functions are available since birth of C. So, if I don't do it and experience the "danger" and shortcomings of functions myself, I'd never really understand why professionals discourage some C functionalities. Moreover, I think those functions like some keywords we're not using (auto and goto for example) are not out there "for nothing", meaning in some context we should use gets() instead of fgets() .. just a thought because of a belief the designer and engineers of C were not stupid !

    if I do man 3 gets in linux I get DEPRECATED and never use this function and so on ... lol!

    then a question pops up in my head: why is it there in first place ?

    kind regards

  14. #14
    Registered User
    Join Date
    Jun 2011
    Posts
    4,508
    Quote Originally Posted by yvan View Post
    dear Matticus,
    thank you for the answers and explanation but I get this feeling you are addressing me as if I am a developer of some real system/software, which is not the case because I am learning, trying to discover and understand some C.
    Not at all - I try to gauge my advice on the perceived level of the person asking the question. If anything I described was confusing, let me know - I would be happy to clarify.

    Quote Originally Posted by yvan View Post
    so, I have some problems when I hear or read people saying don't use x() don't use y() while these functions are available since birth of C. So, if I don't do it and experience the "danger" and shortcomings of functions myself, I'd never really understand why professionals discourage some C functionalities. Moreover, I think those functions like some keywords we're not using (auto and goto for example) are not out there "for nothing", meaning in some context we should use gets() instead of fgets() .. just a thought because of a belief the designer and engineers of C were not stupid !
    The link I posted above explains why "gets()" is bad, and should never be used. You're better off knowing the dangers associated with this function, and learning the safer alternatives, earlier in your programming endeavors, so you don't have to "unlearn" bad habits when you get more experienced.

    Quote Originally Posted by yvan View Post
    if I do man 3 gets in linux I get DEPRECATED and never use this function and so on ... lol!
    Yes - that's another reason not to use it.

    Quote Originally Posted by yvan View Post
    then a question pops up in my head: why is it there in first place ?
    "gets()" is a relic from the early days of C. Back then, it served its purpose and its dangers were not a big concern. However, as the language proliferated and becamed more commonly used, security concerns required safer alternatives. The link I posted above contains another link that explains some of the dangers associated with "gets()".

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. XOR encryption is null-terminating my strings!
    By steez in forum C Programming
    Replies: 6
    Last Post: 05-31-2011, 04:16 PM
  2. Check terminating null character
    By Ducky in forum C++ Programming
    Replies: 2
    Last Post: 10-10-2010, 03:07 AM
  3. null terminating a string
    By steve1_rm in forum C Programming
    Replies: 5
    Last Post: 03-01-2009, 05:55 PM
  4. Null terminating a character pointer?
    By INFERNO2K in forum C++ Programming
    Replies: 9
    Last Post: 11-28-2005, 08:52 AM
  5. terminating null
    By rodrigorules in forum C Programming
    Replies: 19
    Last Post: 09-09-2005, 01:43 AM