0

What I'm trying to do is learn how to use pointers. Though the outputs of the this small program are controversial:

OUTUPTS

hello World!
[STRING]:hello World!
³a

y = x^2+y^2+2ax+2by+c
[STRING]:y = x^2+y^2+2ax+2by+c
­v¿▄▲_

Everything is fine, exept the last chars at the end. I run the program multiple times with differnt inputs: the string remais fine, but still the chars are there, changing some times.

/*C program to input and print arrays using pointers*/
#include <stdio.h>
#define STACK   1000

int main(){
  /*Variables*/
  char t[STACK];
  char *pt;
  int c, i;
  /*Input*/
  for(i = 0, pt = t; *pt < STACK && *pt != '\n' && (c = getchar()) != EOF; i++){
     *(pt + i) = c;
     if(*pt == '\n')
        *(pt++) = '\0';
}
 /*Print*/
 printf("[STRING]:%s", t);

}

If you know what the error is, please give me an hint :> Thanks in advance.

  • Unless the very first character you enter is a newline, `*pt` will never be equal to `'\n'`. So you never NUL-terminate the string that you are reading and print whatever garbage is after the end when you get to EOF. – Chris Dodd Apr 30 '21 at 05:36
  • A few links that provide basic discussions of pointers may help. [Difference between char *pp and (char*) p?](https://stackoverflow.com/a/60519053/3422102) and [Pointer to pointer of structs indexing out of bounds(?)...](https://stackoverflow.com/a/60639540/3422102) (ignore the titles, the answers discuss pointer basics) Also, `*pt < STACK` isn't doing what you think it is. `*pt` isn't a counter and will always be less than `1000` being type `char` (whether signed or unsigned) – David C. Rankin Apr 30 '21 at 05:52
  • `for(i = 0, pt = t; *pt < STACK && *pt != '\n' && (c = getchar()) != EOF; i++){` really? why? I hate the acronym but I just have to give you this link: https://en.wikipedia.org/wiki/KISS_principle – Support Ukraine Apr 30 '21 at 06:27

2 Answers2

2

In addition to the links provided to pointer basics in the comment, you are confusing yourself by overloading your loop definition which can obscure individual tests and assignments.

The key in taking any character input while filling an array is knowing when to stop taking input in order to protect your array bounds. Since you must reserve room at the end of your input for the nul-terminating character '\0' (or just plain 0), the last array index you can fill with input is STACK-2. The easiest way to properly limit input is simply to keep a counter, initialized to zero at the start.

You cannot use *pt < STACK as a test to determine when the array is full (or for anything else for that matter). Since *pt is type char its value will always be less than 1000 making the comparison *pt < STACK superfluous.

An array declaration is complete when made, so to initialize pt, you can simply do that at the time of declaration, e.g.

#include <stdio.h>

#define STACK   1000                /* good job defining a constant! */

int main (void) {
    
    char t[STACK], *pt = t;         /* array and pointer */
    ...

Your declaration of c as type int is correct and required to properly test against EOF. Do not get in the habit of using *(pt + i) for pt[i]. Yes, they are equivalent, but the first is less readable for many.

To structure your read, you can simply do:

    ...
    int c;
    size_t n = 0;                   /* use counter */
    
    fputs ("enter characters, [Enter] when done:\n\n[INPUT ]: ", stdout);
    
    /* while t not full and getchar not EOF */
    while (n + 1 < STACK && (c = getchar()) != '\n' && c != EOF) {
        *pt++ = c;                  /* assign character to pointer, advance */
        n++;                        /* increment counter */
    }
    ...

All that remains is nul-terminating t and outputting the results, e.g.

    ...
    *pt = 0;                        /* nul-terminate t */
    
    printf("[STRING]: %s\n", t);    /* output result */
}

Altogether, you would have:

#include <stdio.h>

#define STACK   1000                /* good job defining a constant! */

int main (void) {
    
    char t[STACK], *pt = t;         /* array and pointer */
    int c;
    size_t n = 0;                   /* use counter */
    
    fputs ("enter characters, [Enter] when done:\n\n[INPUT ]: ", stdout);
    
    /* while t not full and getchar not EOF */
    while (n + 1 < STACK && (c = getchar()) != '\n' && c != EOF) {
        *pt++ = c;                  /* assign character to pointer, advance */
        n++;                        /* increment counter */
    }
    
    *pt = 0;                        /* nul-terminate t */
    
    printf("[STRING]: %s\n", t);    /* output result */
}

Example Use/Output

$ ./bin/getchar_pt
enter characters, [Enter] when done:

[INPUT ]: My dog has fleas, my cat has none!
[STRING]: My dog has fleas, my cat has none!

Look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Thanks a lot. This was very usefull –  Apr 30 '21 at 19:24
  • Hey David. I am lost. see code: https://godbolt.org/z/adfq9E76a. I commented out `*pt = 0` now &t return the same result as &pt. and printf t return the stdin string. but printf(pt) return NULL. I thought they are the same.. – jian Aug 01 '22 at 10:26
  • @jian - all functions that expect a C-string, expect a nul-terminated string. The type for `NULL` is a pointer, not character. So while `'\0'` and `0` are the same (ASCII `0` and plain `0`), `NULL` (while it has a defined value of `0`) it has a pointer type, not character type. So nul-terminated means terminated by the nul-terminating character, not by a `NULL` pointer. `*pt = 0;` is what nul-terminates the string. Best exercise is take the example `"cat"` and step through the loop and write it out on paper. – David C. Rankin Aug 02 '22 at 04:39
0
#include <stdio.h>
#define STACK   1000

int main(){
    /*Variables*/
    char t[STACK];
    char *pt;
    int c, i;
    /*Input*/
    for(i = 0, pt = t; i < STACK && *(pt + i) != '\n' && (c = getchar()) != EOF; i++) { 
       // If the array is out of bounds, you should use i to judge, your pt has not changed, you should add i to judge again 
        *(pt + i) = c;
    }
    *(pt + i) = '\0'; // Add zero char at the end after exiting the loop 
    /*Print*/
    printf("[STRING]:%s", t);
    return 0; // Please note that your main function return value is int 
}
Hancel Lin
  • 345
  • 2
  • 10
  • Thanks for the "bounding input" concept. It was like an enlightment! –  Apr 30 '21 at 19:25