简体   繁体   中英

Why can't I use string functions like scanf() or gets() strcat() strcpy() with character pointer variables?

I don't see why I keep getting a segmentation fault when I try to use ANY string function with a character pointer variable. I am fully aware that fgets() is better to ensure no overwriting occurs but I am just trying to write a program to demonstrate how string functions actually overwrite data in memory addresses whereas re-declaring the pointer variable ie pValues = "New Name"; will point to a completely different set of addresses (that contain your required characters).

I know fully well that string functions do seem to work with pointer constants ie when defined like this:

char values[ ] = "Name Here";

BUT WHY do they not work for pointer variables ie when defined like this:

char *pValues[ ] = "Name Here";

this is the part that keeps failing...

#include <stdio.h>
#include <string.h>     // required for strcpy(), strcat(), strlen()

main()

{
    
    char *pName = "Peter Jones";
    
    printf("The string pName is \'%s\'\n\n", pName);
    
    // Now lets change it to a longer string with more litterals...
    
    pName = "Dragons Den is GREAT!";
    
    printf("\nThe string pName is %s\n", pName);
    
    puts("Please enter a new string for pName ");
    gets(pName);
    
    printf("pName is now %s\n", pName); // THIS PRODUCES SEGMENTATION FAULT
    
    return 0;
    
}

The book I am learning from has NO mention of dynamic memory or heap - which I haven't yet covered.

BUT WHY do they not work for pointer variables ie when defined like this:

char *pValues[ ]= "Name Here";

Because pValues is not a character pointer. It is an array of character pointers. If instead you do this:

char *pValues = "Name Here" ;

most of the functions you want to use will work just fine. The one exception is any function that tries to write to any memory pointed to by this pointer:

    puts("Please enter a new string for pName ");
    gets(pName);
    
    printf("pName is now %s\n", pName); // THIS PRODUCES SEGMENTATION FAULT

This part fails because gets() writes to a pointer that is pointing to a protected part of memory. You initialized it with:

    char *pName = "Peter Jones";

so pName points to memory that holds a string literal. As you saw, attempting to write to this area of memory results in a segfault.

You will need to learn more about dynamic memory allocation and the heap in order to use gets() with a pointer.

"Why can't I use string functions like scanf() [etc.] with character pointer variables?"

You can , but only by following the rules:

  • the type defining the variable must be matched with the correct form of initializer .
  • string literal initializers to pointer variables create not editable variables
  • string literal initializers to char arrays are editable but not resizable.

The code example posted violates each of these.

This for example:

char values[] = "Name Here";

creates an editable C string with the implicit length set to sizeof("Name Here") . Because the size of the array is implicit (not specified), the initializer is required. If however the array size is explicit, the initializer is optional.:

//either line is legal, and both resulting variables are editable
char values[20];
char values2[20] = "Name Here";

 

This:

char *pValues[]= "Name Here";

Is an array of pointers to char , which expects an initializer list , which can be accomplished by surrounding the string literal(s) with {...} :

char *pValue[] = {"Name Here:"};
char *pValues[] = {{"Name Here:"},{"Address Here:"}};//see image
char *pValues2[] = {{"Name Here:"},{"Address Here:"}, ...};

resulting in from one to many non-editable C strings, each stored at a separate location in memory.

The expression:

char *pValue3 = "Name Here:";  //create non-editable variable

also places the contents of the string literal "Name Here:" into a memory location, and like the previous example, is in a non-writable area of memory . Once it is initialized at creation, it is no longer editable.

For example, given:

char *var1 = "this is a non-editable string"; 
char var2[80] = {"This is an editable string"};

strcpy(var1, var2);//is illegal
strcpy(var2, var1);//is legal

The following image corresponds to char *pValues[] above:
在此处输入图像描述

pName is a pointer. You have assigned it it with the reference to the string literal. String literals cannot be modified - thus segfault which is one of the possible outcomes of the invoked UB.

You need to allocate some space for the string.

you can:

char str[100];
pName = str;
/* or */
pName = malloc(100);

/*or even*/
pName = (char[100]){};

/* then */

fgets(pName, 100, stdin);

do not use gets function as it is dangerous.

Adrian, I do not see why you "keep getting a segmentation fault when I try to use ANY string function with a character pointer variable" either... And I do not know how to judge scanf() over fgets() to ensure anything.

But back to this issue in particular, maybe I can help you. There some more things you also need to be "fully aware" of:

When you write

char *pName = "Peter Jones";

you are defining a string literal. pName will then point to an area in memory that has "Peter Jones" in the successive addresses following pName . Yes, pName is in some sense a pointer to char, but it is a pointer pointing to a constant area. You can make pName point to another address but can not use it to change the "Peter Jones" area thing. It is there to stay.

So when you write

// Now lets change it to a longer string with more literals...  
  
     pName = "Dragons Den is GREAT!";

It will succeed... But maybe it should not. When you do this you are saying Goodbye to the string "Peter Jones" . May be harmless but it is sort of a memory leak in the sense that it is there and it is lost.

But when you call gets() things are different: you are trying to write over a constant literal. You can not do that and you program is gone.

This has nothing to do with heap or stack or dynamic allocation. Just allocation. You need to have some writable area to point gets() to write to.

Example

char qName[40];
char* pName = "Peter Jones";
strcpy(qName, pName);
printf("The string pName is \'%s\'\n\n", pName);
// Now lets change it to a longer string with more litterals...
pName = "Dragons Den is GREAT!";
printf("\nThe string pName is %s\n", pName);
puts("Please enter a new string for qName ");
fgets(qName,10,stdin);
pName = qName;
// now pName points to the data read by gets()
printf("pName is now %s\n", pName);

Now it works. Declaring

char qName[40];

we have now 40 bytes available to use. qName is a pointer to an area of RAM of 40 contiguous bytes. In some sense it is a pointer to char.

But now you can tell gets() to read into qName . No problem here. You can even point pName to qName . But then another area is lost: the area where "Dragons Den is GREAT!" is gone.

By the other hand, if you declare

char* p = NULL;

Now you have a pointer to char. It is now pointing to nothing, but at any time you can dynamically allocate memory and point p to that, ou make p point do an area of your program, like writing

p = qname + 2;

and then have p pointing to the third byte of qName .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM