C 发送参数的地址安全吗?
考虑以下代码段:C 发送参数的地址安全吗?,c,function,arguments,parameter-passing,C,Function,Arguments,Parameter Passing,考虑以下代码段: void read_write(unsigned* ptr, bool r_or_w) { if (r_or_w) { *ptr = read_from_device_register(); } else { write_to_device_register(*ptr); } } unsigned read(void) { unsigned data = 0; read_write(&data,
void read_write(unsigned* ptr, bool r_or_w) {
if (r_or_w) {
*ptr = read_from_device_register();
} else {
write_to_device_register(*ptr);
}
}
unsigned read(void) {
unsigned data = 0;
read_write(&data, 1);
return data;
}
void write(unsigned data) {
read_write(&data, 0); // <--- sending address of received argument
}
在这两种情况下,
数据
存储在堆栈上,只有在第一种情况下,数据
存储在调用方的调用帧上,而不是作为局部变量存储在被调用方中。安全吗?有什么陷阱吗?如果有的话,它有多常见?只要read\u write
不将指针存储在比write
框架中的指针对象数据
寿命更长的地方,这里的生存期就没有问题。因此,从程序正确性的角度来看,这是安全的。事实上,给定的两个选项之间没有区别——传入参数的地址可以采用,就像局部变量的地址可以采用一样。至于在write
的调用者的框架中修改变量的问题,这也是不相关的-数据
是按值传递的
但是,如果函数经常调用或在紧循环中调用,则无符号整数的指针间接寻址可能会带来很大的开销
此外,从设计的角度来看,这似乎是一个有点难以理解的尴尬设计<代码>写入委托给
读写
,最终委托给写入设备寄存器
。这有点难以直观地理解,而且不必要地将读写路径混合在一起,只是为了让它们再次分开;这可能是不安全的,因为与更简单的设计相比,代码更脆弱,更容易维护错误。只要read\u write
不将指针存储在比write
框架中的指针对象数据
寿命更长的地方,这里的生命周期就没有问题。因此,从程序正确性的角度来看,这是安全的。事实上,给定的两个选项之间没有区别——传入参数的地址可以采用,就像局部变量的地址可以采用一样。至于在write
的调用者的框架中修改变量的问题,这也是不相关的-数据
是按值传递的
但是,如果函数经常调用或在紧循环中调用,则无符号整数的指针间接寻址可能会带来很大的开销
此外,从设计的角度来看,这似乎是一个有点难以理解的尴尬设计<代码>写入委托给
读写
,最终委托给写入设备寄存器
。这有点难以直观地理解,而且不必要地将读写路径混合在一起,只是为了让它们再次分开;这可能是不安全的,因为与更简单的设计相比,代码更脆弱,更容易维护错误。语言规范没有说明参数存储的位置;关于哪个堆栈帧保存地址,您所描述的是一个实现细节,它不是语言的一部分。C甚至没有指定有一个堆栈,尽管它是典型的实现
变量的位置与回答这个问题无关,只是变量的生存期。函数参数的生存期与函数顶级块中的局部变量相同。您可以将地址传递给另一个函数,只要它只在write()
返回之前使用,地址就有效。如果地址保存在其他数据结构中,则在write()
返回后使用它会导致未定义的行为
因此,就您的问题而言,使用
&w_data
和&data
没有区别。一个好的编译器优化器甚至可能为两个版本生成相同的代码。语言规范没有说明参数存储在哪里;关于哪个堆栈帧保存地址,您所描述的是一个实现细节,它不是语言的一部分。C甚至没有指定有一个堆栈,尽管它是典型的实现
变量的位置与回答这个问题无关,只是变量的生存期。函数参数的生存期与函数顶级块中的局部变量相同。您可以将地址传递给另一个函数,只要它只在write()
返回之前使用,地址就有效。如果地址保存在其他数据结构中,则在write()
返回后使用它会导致未定义的行为
因此,就您的问题而言,使用&w_data
和&data
没有区别。一个好的编译器优化器甚至可能为两个版本生成相同的代码。这是绝对安全的
即使读写(&data,0)
使用指针更改数据的值
,该更改不会影响无效写入(无符号数据){..}
之外的任何内容。函数中的无符号数据
与局部函数变量一样,只是它由调用函数时使用的参数值初始化
这是C中一个简单但重要的特性:参数总是按值传递。函数不能更改原始参数的值。如果参数是指针,则函数可以更改指向对象的值,但不能更改参数本身的值
例如:
void f1(int* p)
{
*p = 42;
}
void f2(int n)
{
printf("At start of f2 n is %d\n", n);
f1(&n);
printf("At end of f2 n is %d\n", n);
}
void f3(int* p)
{
printf("At start of f3 *p is %d\n", *p);
f2(*p);
printf("At end of f2 *p is %d\n", *p);
}
调用上面的链式
int x = 17;
printf("Before f3 x is %d\n", x);
f3(&x);
printf("After f3 x is %d\n", x);
将产生(我的评论):
绝对安全
即使读写(&data,0)
使用指针更改数据的值
,该更改不会影响无效写入(无符号数据){..}
之外的任何内容。函数中的无符号数据
与局部函数变量一样,只是它由调用函数时使用的参数值初始化
这是C中一个简单但重要的特性:参数总是按值传递。有趣的
int x = 17;
printf("Before f3 x is %d\n", x);
f3(&x);
printf("After f3 x is %d\n", x);
Before f3 x is 17
At start of f3 *p is 17
At start of f2 n is 17
At end of f2 n is 42 // notice: f1 changed the value of n inside f2
At end of f3 *p is 17 // but when f2 returns the value of the original x is the same
After f3 x is 17 // Reason: x and n are to different variables