Passing parameters with string pointer and character array
I was beaten on the written test with a convinced person. I was very impressed by a question of using a pointer to make formal parameters. I used this to make up for the pointer all night and summarized it today as a warning.
Imagine that there is the following situation where a string pointer is used as a formal parameter assignment function to modify its string. Will the string be changed after the function ends?
#include<>
void testPstr(char *ppstr){
ppstr = "hasten";
printf("%s\n",ppstr);
}
int main(){
char *pstr = "test";
printf("%s\n",pstr);
testPstr(pstr);
printf("%s\n",pstr); // test ? hasten?
}
The result is nothing.
test
hasten
test
I used to naively think that pointers are essentially addresses. Passing pointers to functions is actually passing addresses to functions to operate. In fact, this is not the case. The logic of the compiler here is, pstr is essentially a local variable in the main() function, andIt is obvious that functions cannot use variables from other functions across functions.(Obviously, there will be security issues when doing this). What should I do? That is to copy the same variable in the function and accept the value of the actual parameter to operate.
For pointers, its value is the address. The pass parameter is essentially a new pointer defined in the function (here is pstr) pointing to the same address. However, unlike the passing of plastic variables such as int, double, and char, char * points to a character array, which is stored in the program's .rodata segment, that is, the area where read-only data is stored. This is why directly modifying the string referenced by char * will cause SF:
char *pstr = "test";
pstr[0] = 'Z'; // segment fault
printf("%s\n",pstr);
The program cannot allow you to modify the data in this memory, soppstr = "hasten"
This operation essentially changes a pointer pstr that has nothing to do with main from pointing to the same memory segment as pstr in rodata to another area where the "hasten" string is stored. At the end of the function, pstr is destroyed in the stack and will not affect the pstr in main().
pstr->"test"<-ppstr
pstr->"test"
ppstr->"hasten"
Verification is as follows:
#include<>
void testPstr(char *ppstr){
printf("ppstr_cont:%s ppstr_cont_addr:%#x ppstr_addr:%#x\n",ppstr,ppstr,&ppstr);
ppstr = "hasten";
printf("ppstr_cont:%s ppstr_cont_addr:%#x ppstr_addr:%#x\n",ppstr,ppstr,&ppstr);
}
int main(){
char *pstr = "test";
printf("pstr_cont:%s pstr_cont_addr:%#x pstr_addr:%#x\n",pstr,pstr,&pstr);
testPstr(pstr);
printf("pstr_cont:%s pstr_cont_addr:%#x pstr_addr:%#x\n",pstr,pstr,&pstr);
}
result:
pstr_cont:test pstr_cont_addr:0x404039 pstr_addr:0x61fe18
ppstr_cont:test ppstr_cont_addr:0x404039 ppstr_addr:0x61fdf0
ppstr_cont:hasten ppstr_cont_addr:0x404032 ppstr_addr:0x61fdf0
pstr_cont:test pstr_cont_addr:0x404039 pstr_addr:0x61fe18
My operating environment here is Windows. I have tried Linux before, but the change in the running address is larger than that of Win, which is difficult to verify, so I changed it to Windows, but the essence is similar.
You can see that the address 404 starts with a constant storage area, and the beginning of 61f starts with a stack area. The first character t of the string test pointed to by pstr is stored at 0x404039, and its pointer itself is 0x61fe18. The other pointer copied after the function is passed is 61fdf0. It was originally pointing to the address 0x404039 where the test first character is located. The hasten first character t pointed to is stored at 0x404000, and since then it is not related to pstr.
So how do you assign the modified value to pstr? The method is to use a secondary pointer:
#include<>
void testPstr(char **ppstr){
printf("ppstr_cont:%s ppstr_cont_addr:%#x ppstr_addr:%#x\n",*ppstr,ppstr,&ppstr);
*ppstr = "hasten";
printf("ppstr_cont:%s ppstr_cont_addr:%#x ppstr_addr:%#x\n",*ppstr,ppstr,&ppstr);
}
int main(){
char *pstr = "test";
printf("pstr_cont:%s pstr_cont_addr:%#x pstr_addr:%#x\n",pstr,pstr,&pstr);
testPstr(&pstr);
printf("pstr_cont:%s pstr_cont_addr:%#x pstr_addr:%#x\n",pstr,pstr,&pstr);
}
In this way, the address of pstr is passed. The pstr in the function points to pstr. The memory that the rvalue calls these pointers actually point to is as follows. The specific result will not be placed. You can actually verify it:
Memory before entering function: pstr->pstr->"test"
Memory after entering the function: pstr->pstr->"ppstr"
Quote: &ppstr pstr *ppstr
&pstr pstr
Another method is to change testPstr to a function that returns a char* pointer, so I won't say much.
The difficulty of C-language pointers is that they have many syntax specifications (*, &), and combined with lvalue references and rvalue references, the overall use becomes more cumbersome. For the same pointer syntax, a typical example is a linked list (p->next as the lvalue represents the address to be pointed to, and p->next as the rvalue represents the address to be pointed to by p). You must be more cautious when using it normally.
Go back to the example in the figure above. If you change char *pstr to char pstr[], the formal parameters of the function are fromchar *
Change tochar []
, What will happen?
void testPArr(char pstrArr[]){
pstrArr[0] = 'Z';
printf("pstrArr in test: pstrArr_cont:%s pstrArr_cont_addr:%#X pstrArr_addr:%#X\n\n", pstrArr, pstrArr, &pstrArr);
}
int main(){
//Continuous Type
char pstr[] = "test";
printf("pstr in main: pstr_cont:%s pstr_cont_addr:%#X, pstr_addr:%#X\n\n",pstr,pstr,&pstr);
testPArr(pstr);
printf("pstr in main: pstr_cont:%s pstr_cont_addr:%#X, pstr_addr:%#X\n\n",pstr,pstr,&pstr);
}
pstr in main: pstr_cont:test pstr_cont_addr:0X61FE1B, pstr_addr:0X61FE1B
pstrArr in test: pstrArr_cont:Zest pstrArr_cont_addr:0X61FE1B pstrArr_addr:0X61FDF0
pstr in main: pstr_cont:Zest pstr_cont_addr:0X61FE1B, pstr_addr:0X61FE1B
You can see that the modification of the character array in the function is to operate on the original address of pstr. The reason is that pstr is a character array with the content "test" stored at this time. All its data is in the user stack area, so it can be modified.When it is used as a function parameter, the compiler will parse it into a pointer to the first address of its first element.Thereforevoid testPArr(char pstrArr[])
Equivalent tovoid testPArr(char *pstrArr)
, You can also see that it has an independent pointer address in the running result, which is a pointer to a character array. Modifications made through pointer subscript references will affect the character array.
But essentially, the function still copies the incoming data pointer that only works in the function, so if the above program is allowedpstrArr = "Hasten"
If you want to let the pointer give up the character array originally pointed to from the user stack area, and point to another string literal from the .rodata segment, and part ways with pstr.
What if you want to modify the contents in the character array? Obviously not allowedpstr = "xxx"
, because pstr is an array of data that has been allocated, it is the header of this piece of data, not a pointer, and it cannot directly refer to the string literal. The only way is to use a loop to copy the characters one by one and allocate them to memory.
char pstr[] = "test";
char data[] = "data";
pstr = data; //error
for(int i = 0; i < sizeof(data)/sizeof(data[0]); i++){
pstr[i] = data[i];
}
Obviously, character arrays and character pointers have their own characteristics. For example, if you want to set the access attribute of a string to read-only, you can use it.const char *
, and if you want to read and write, you can use itchar x[]
。
Summarize:
-
The function cannot directly use the variables of other functions, but copy the actual parameter variable. Therefore, the function can copy the pointer parameter and point to the address of the actual parameter to modify the value of the address where the actual parameter variable is located.
-
Character arrays and character pointers have different properties. The data of the character array is the array itself, which belongs to the stack segment and can be modified. Character pointers refer to strings from .rodata segments. Modifying the strings referenced will cause program errors (that is, why in practice, char* is defined as
const char*
) -
In C language, when a one-dimensional array is used as a function parameter, the compiler always parses it into a pointer to its first element first address.
refer to
"C and Pointer" Pointer Part
C language array parameters and pointer parameters