1. 指针传参和接收返回值对指针的影响
1.1 题1
#include<stdio.h>
//注意堆区和栈区中变量的区别!!!!!
void test(int* str){
int a = 10;
str = &a;
}
int main()
{
int b = 20;
int *p2 = &b;
test(p2);
printf("p2的%d",*p2);
}
在 test 函数内部,你定义了一个局部变量 a,并尝试将指针 str 指向 a 的地址。这是有效的,但请注意,这不会影响 main 函数中的 p2,因为 str 变量只是 test 函数的局部变量,它的改变不会影响 p2。
test 函数返回,a 的生命周期结束,它的内存被释放。此时,str 不再指向有效的内存地址。
在 main 函数中,你尝试访问 *p2,这实际上是访问 b 的值,因为 p2 一直指向 b 的地址。你无法访问 a 的值,因为它的内存已经被释放。
如果你想在 main 函数中访问 a 的值,你需要更改你的代码逻辑,例如,将 a 声明为全局变量,或者通过动态内存分配来管理它的生命周期。
1.2 题2
#include<stdio.h>
#include<stdlib.h>
//注意堆区和栈区中变量的区别!!!!!
void test2(int* str){
int* c = (int*)malloc(sizeof(int));
*c = 30;
str = c;
}
int main()
{
int b = 20;
int* p2 = &b;
test2(p2);
printf("p2的%d",*p2);
}
注意指针的传递
对于题二的更改要么设置返回值,要么传地址
1.3 题3
#include<stdio.h>
//注意堆区和栈区中变量的区别!!!!!
int* test2(){
int* c = (int*)malloc(sizeof(int));
*c = 30;
return c;
}
int main()
{
int b = 20;
int* p2 = &b;
p2 = test2();
printf("p2的%d",*p2);
free(p2);
}
代码正确
动态分配的内存再堆上不会自动释放
1.4 题4
#include<stdio.h>
#include<stdlib.h>
//注意堆区和栈区中变量的区别!!!!!
void test(int** str){
//不安全的
// int a = 10;
// *str = &a;
//安全的写法
int* a = (int*)malloc(sizeof(int));
*a=10;
*str = a;
}
int main()
{
int b = 20;
int *p2 = &b;
test(&p2);
printf("p2的%d",*p2);
}
存在悬空指针问题,不安全,避免这种写法
使用第二种写法
1.5 题5
#include<stdio.h>
//注意堆区和栈区中变量的区别!!!!!
int* test2(){
int c = 30;
int* str = &c;
return str;
}
int main()
{
int b = 20;
int* p2 = &b;
p2 = test2();
printf("p2的%d",*p2);
}
当你在一个函数中创建一个局部变量(比如 int c = 30;),这个变量通常会分配在栈内存中。当函数执行完毕,栈上的局部变量会被销毁,这是栈的典型行为。这意味着局部变量 c 的内存将在 test2 函数结束时被释放,不再可访问。
这种情况可以分为两种
1. c被保存到寄存器且p2拿到的也是寄存器的地址
然而,编译器可以进行优化,有时候它会将局部变量 c 的值保存在寄存器中,而不是栈上。
因此它可以将其保存在寄存器中以提高代码执行的效率。这个过程称为寄存器分配优化。
如果 c 的值被保存在寄存器中,那么返回 &c(c 的地址)给 main 函数实际上是返回寄存器中的地址,而不是栈上的地址。所以可以访问到c
2. c是被销毁了,但是p2拿到了地址,可能是个悬空指针,但 p2 仍然包含了之前的地址。尝试访问 c 的值可能会导致未定义的行为,因为它指向的内存可能已经被其他数据覆盖。但是这期间由于某种原因,被销毁的变量的内存内容仍然可以保留一段时间,c这个地址还没有被覆盖导致你依然可以访问到
但是,这种行为是不可靠的,因为它依赖于编译器和编译器优化的具体实现。不同的编译器和编译选项可能会导致不同的结果。因此,为了编写可移植且健壮的代码,最好避免返回指向局部变量的指针,因为这可能导致未定义的行为。
由于这两种情况都可能发生,代码的行为是不确定的。这就是为什么悬挂指针是一个不安全的编程实践,因为它依赖于编译器的实现细节和优化。你应该避免使用悬挂指针,以确保代码的可靠性和可移植性。如果你需要跨函数访问局部变量的值,最好使用全局变量、堆内存分配或其他方法来确保数据的有效性。
1.6 题6
#include<stdio.h>
void test(int* str){
int a = 10;
str = &a;
}
int main()
{
int* p1=NULL;
test(p1);
printf("p1的%d",*p1);
}
虽然p1是指针,指向了NULL,也传递了 ,但是函数的str只是p1的拷贝临时的,虽然和p1一样指向NULL,但是他要改变指向并不会影响p1
2. 计算字符串大小的坑
void test(char arr[]){
printf("%d\n",sizeof(arr));//这里计算的是指针大小 8
}
int main()
{
// char arr[] = "abcd";
test("abcde");
return 0;
}
计算的是指针大小
void test(char arr[]){
printf("%d\n",sizeof(arr));
}
int main()
{
char arr[] = "abcd";
test(arr);
return 0;
}
在C语言中,数组作为函数参数传递时,它会被转换成指针类型。因此,在函数test中,sizeof(arr)实际上返回的是指针的大小,而不是数组arr的大小。所以即使arr在main函数中是一个字符数组,它在test函数中被视为指针。因此,在test函数中,sizeof(arr)返回的是指针的大小,而不是数组的大小。
在这种情况下,sizeof(arr)返回的大小通常是指针大小,即4或8字节(具体取决于你的系统,32位系统一般是4字节,64位系统一般是8字节)。
如果你想在test函数中获得数组的大小,你可以传递数组的长度作为参数,或者使用C语言中的一些特殊技巧,比如使用一个标记来表示数组的结尾。