我试图了解C指针。作为背景,我习惯于在C#
和Python3
中进行编码。
我知道可以使用指针来保存变量的地址(写type* ptr = &var;
之类的东西),并且递增指针等同于递增该对象类型{{1}的对象数组的索引}。但是我不明白的是,您是否可以使用type
(例如type
)的指针和引用对象而没有引用已经定义的变量。
我想不出一种方法,大多数C / C ++指针示例似乎都使用它们来引用变量。因此,可能是我要问的是不可能的和/或不好的编码实践。如果是这样,则有助于理解原因。
例如,为了澄清我的困惑, if 如果不使用预定义的硬编码变量就无法使用指针,为什么要使用指针而不是直接使用基本对象或数组?对象?
下面有一小段代码正式描述了我的问题。
非常感谢您的任何建议!
int
答案 0 :(得分:6)
是的,您可以使用动态内存(也称为“堆”)分配:
#include <stdlib.h>
int * const integer = malloc(sizeof *integer);
if (integer != NULL)
{
*integer = 4711;
printf("forty seven eleven is %d\n", *integer);
free(integer);
// At this point we can no longer use the pointer, the memory is not ours any more.
}
这要求C库从操作系统分配一些内存并返回指向它的指针。分配sizeof *integer
个字节可以使分配恰好适合整数,然后我们可以使用*integer
取消对指针的引用,这几乎就像直接引用整数一样。
答案 1 :(得分:1)
在C语言中,函数的参数始终按值传递,因此在函数中更改参数值不会反映在调用者中。但是,您可以使用指针来模拟按引用传递。例如:
void clear(int *x)
{
*x = 0;
}
int main()
{
int a = 4;
printf("a=%d\n", a); // prints 4
clear(&a);
printf("a=%d\n", a); // prints 0
return 0;
}
您还可以使用指针指向动态分配的内存:
int *getarray(int size)
{
int *array = malloc(size * sizeof *array);
if (!array) {
perror("malloc failed");
exit(1);
}
return array;
}
这些只是几个例子。
答案 2 :(得分:1)
在C语言中使用指针有很多充分的理由,其中之一是,您只能在C语言中通过 value 传递-您不能通过引用传递。因此,将指针传递到现有变量可以节省将其复制到堆栈的开销。作为一个例子,让我们假设这个非常大的结构:
struct very_large_structure {
uint8_t kilobyte[1024];
}
现在假设需要使用此结构的函数:
bool has_zero(struct very_large_structure structure) {
for (int i = 0; i < sizeof(structure); i++) {
if (0 == structure.kilobyte[i]) {
return true;
}
}
return false;
}
因此要调用此函数,您需要将整个结构复制到堆栈中,尤其是在嵌入式平台上,其中C被广泛使用,这是不可接受的要求。
如果您将通过指针传递结构,则仅将指针本身(通常是32位数字)复制到堆栈中:
bool has_zero(struct very_large_structure *structure) {
for (int i = 0; i < sizeof(*structure); i++) {
if (0 == structure->kilobyte[i]) {
return true;
}
}
return false;
}
这绝不是唯一,最重要的指针用法,但它清楚地说明了为什么指针在C语言中很重要的原因。
答案 3 :(得分:1)
但是我不明白的是,您是否可以使用指针和类型(例如int)的引用对象而无需引用已经定义的变量。
是的,有两种情况是可能的。
第一种情况是动态内存分配。您可以使用malloc
,calloc
或realloc
函数从动态内存池(“堆”)分配内存:
int *ptr = malloc( sizeof *ptr ); // allocate enough memory for a single `int` object
*ptr = some_value;
第二种情况是,您为I / O通道或端口或某物指定了一个固定的,定义明确的地址:
char *port = (char *) OxDEADBEEF;
尽管这在嵌入式系统中比普通的应用程序编程更为常见。
编辑
关于第二种情况,chapter and verse:
6.3.2.3指针
...
5整数可以转换为任何指针类型。除先前指定外, 结果是实现定义的,可能未正确对齐,可能未指向 引用类型的实体,并且可能是陷阱表示。 67)
67)用于将指针转换为整数或将整数转换为指针的映射函数旨在 与执行环境的寻址结构一致。
答案 4 :(得分:0)
最常见的原因:因为您希望在不传递内容的情况下修改内容。
类比:
如果您想对客厅进行粉刷,则不想将房子放在卡车拖车上,将其移到油漆工上,让他做,然后拖回去。这将是昂贵且费时的。如果您的房子太宽了,无法在街上拖拉,卡车可能会撞车。您宁愿告诉画家您住的住址,让他去那里做这项工作。
用C表示,如果您有一个大的struct
或类似名称,则需要一个函数在不复制该结构的情况下访问该结构,将一个副本传递给该函数,然后复制回修改后的内容回到原始变量。
// BAD CODE, DONT DO THIS
typedef struct { ... } really_big;
really_big rb;
rb = do_stuff(rb);
...
rb do_stuff (really_big thing) // pass by value, return by value
{
thing->something = ...;
...
return thing;
}
这将复制称为rb
的{{1}}。它被放置在堆栈上,浪费了大量内存,并且不必要地增加了所使用的堆栈空间,从而增加了堆栈溢出的可能性。并且将内容从thing
复制到rb
会花费很多执行时间。然后,当它返回时,您又制作了一个副本,从thing
回到thing
。
通过传递指向该结构的指针,不会进行任何复制,但是最终结果是完全相同的:
rb