【问题标题】:Declare and initialize pointer concisely (i. e. pointer to int)简洁地声明和初始化指针(即指向 int 的指针)
【发布时间】:2014-09-08 10:49:00
【问题描述】:

给定指向 char 的指针,可以执行以下操作:

char *s = "data";

据我了解,这里声明了一个指针变量,为变量和数据都分配了内存,后者用data\0填充,并且有问题的变量设置为指向它的第一个字节(即变量包含一个可以取消引用的地址)。这是简短而紧凑的。

例如,给定指向 int 的指针,可以这样做:

int *i;
*i = 42;

或者那个:

int i = 42;
foo(&i); // prefix every time to get a pointer
bar(&i);
baz(&i);

或者那个:

int i = 42;
int *p = &i;

这有点重复。使用单个变量时,它很小且可以容忍。但是,它不会产生多个变量的多次使用,从而产生代码混乱。

有没有什么方法可以干脆简洁地写出同样的东西?这些是什么? 是否有任何更广泛的编程方法可以完全避免这个问题?可能我根本不应该使用指针(玩笑)还是什么?

【问题讨论】:

  • 在 C 中,函数的参数是按值传递的,而不是按引用(搜索一下),你需要指针来获取后者的行为;并且您需要指针来处理数组(因为在许多情况下它们会衰减为指针)。除此之外,我敢肯定,这是重复的。
  • 这不是有指针的主要原因之一吗?我们传递指针变量(内存地址)的值,而不是它们指向的值。我同意,但我不明白这有什么关系。
  • 对我来说,您的问题似乎是在问(除了其他问题)指针有什么用处(“也许我根本不应该使用指针(笑话)或其他东西”)…抱歉,如果我误会了你。
  • 我对不在 C 中使用指针并不认真。我确实问过是否有任何开箱即用的答案。
  • 啊,现在我知道你的问题了。很抱歉造成混乱。

标签: c pointers dry


【解决方案1】:

字符串文字是一个极端情况:它们触发在静态内存中创建文字,并将其作为char 数组访问。请注意,尽管 42int 文字,但以下内容无法编译,因为它不是隐式分配的:

int *p = &42;

在所有其他情况下,您负责分配指向的对象,无论是在自动内存还是动态内存中。

int i = 42;
int *p = &i;

这里i是一个自动变量,p指向它。

int * i;
*i = 42;

您刚刚调用了未定义的行为。 i 尚未初始化,因此随机指向内存中的某个位置。然后你将42 分配到这个随机位置,结果无法预料。不好。

int *i = malloc(sizeof *i);

这里i 被初始化为指向一个动态分配的内存块。完成后不要忘记free(i)

int i = 42, *p = &i;

这里是你如何创建一个自动变量和一个指向它的指针作为单行。 i 是变量,p 指向它。

编辑:您似乎真的希望该变量被隐式和匿名分配。好吧,你可以这样做:

int *p = &(int){42};

这个东西是一个复合文字。它们是具有自动存储持续时间(或在文件范围内为静态)的匿名实例,并且仅存在于 C90 及更高版本中(但不存在于 C++ 中!)。与字符串文字相反,复合文字是可变的,即您可以修改*p

编辑 2: 添加此解决方案的灵感来自 another answer(不幸的是,它提供了错误的解释)以确保完整性:

int i[] = {42};

这将分配一个具有自动存储持续时间的单元素可变数组。数组名称while not a pointer itself 将根据需要衰减为指针。

但请注意,sizeof i 将返回“错误”结果,即数组的实际大小 (1 * sizeof(int)) 而不是指针的大小 (sizeof(int*))。然而,这应该很少成为问题。

【讨论】:

  • 是否可以像int * i = {42} 之类的,而不是重复变量?
  • @user3849273:不,因为i需要指向一个包含值42的对象
  • @JohnBode 其实……有办法。请继续关注编辑。 (敦敦敦)
  • 谢谢,复合文字似乎是另一种有效的方式。但他们可能更不清楚。也许我应该改用某种宏,比如PINT(i, 42);?但它会掩盖作为函数调用的变量声明,不是吗?
  • @EliasVanOotegem 很高兴。这样的吹毛求疵是每个人都从中受益的一点时间(包括我自己:清晰的答案很难得到正确的答案,熟能生巧)。
【解决方案2】:
int i=42;
int *ptr = &i;

这相当于写

int i=42;
int *ptr;
ptr=&i;

这确实令人困惑,但在函数调用期间它非常有用,因为:

void function1()
{
int i=42;
function2(&i);
}

function2(int *ptr)
{
printf("%d",*ptr); //outputs 42
}

在这里,我们可以很容易地使用这种令人困惑的符号在函数调用期间声明和初始化指针。我们不需要全局声明指针,并在函数调用期间对其进行初始化。我们有一个符号可以同时做这两件事。

int *ptr; //declares the pointer but does not initialize it
//so, ptr points to some random memory location
*ptr=42; //you gave a value to this random memory location

虽然这会编译,但它会调用未定义的行为,因为您实际上从未初始化过指针。

还有,

char *ptr;
char str[6]="hello";
ptr=str;

编辑:正如 cmets 所指出的,这两种情况是不等价的。 但在这两种情况下,指针都指向“你好”。写这个例子只是为了说明我们可以用这两种方式初始化指针(指向 hello),但两者在很多方面肯定是不同的。

char *ptr;
ptr="hello";

作为字符串的名称,str实际上是指向字符串第0个元素的指针,即'h'。 任何数组 arr[] 也是如此,其中 arr 包含第 0 个元素的地址。

【讨论】:

  • 危险的错误。 char str[6]="hello"; 声明了一个 char 数组,其中复制了“hello”。这个数组是自动的和可变的。 OTOH,ptr="hello"; 使 p 指向字符串文字“hello”,它是静态且不可变的。
  • 我说的是指针 ptr,它在这两种情况下都指向一个字符串“hello”。辛苦了,谢谢指点,免得读者迷惑:)
  • 确实两个字符串都读为“hello”,但这不足以使两个代码 sn-ps 等效。假设您要修改字符串,还是 return p; ?差异将显示为崩溃或更糟。所以你绝对应该关心。
  • 我把 Quentin 翻倍了,最后这两种情况是不等价的,这样的想法会导致非常讨厌的运行时错误。请编辑您的答案以反映这一点,否则我将不得不对您投反对票。
  • 已编辑。尽管给出的示例只是为了说明在这两种情况下指针都指向“hello”,但我认为“等价”这个词会导致这种混淆。感谢您指出。
【解决方案3】:
int * i;
*i = 42; 

将调用未定义的行为。您正在修改未知的内存位置。您需要先初始化指针i

int i = 42;
int *p = &i;

是正确的方法。现在p 指向i,你可以修改p 指向的变量。

有没有什么方法可以干脆简洁地写出同样的东西?

没有。由于在 C 中没有通过引用传递,因此当您要修改函数中的 传递的变量 时,您必须使用指针。

是否有更广泛的编程方法可以完全避免这个问题?可能是我根本不应该使用指针(笑话)或什么?

如果你正在学习 C,那么你不能避免指针,你应该学会正确使用它。

【讨论】:

    【解决方案4】:

    您也可以将其视为数组,int i[1]={42} 其中 i 是指向 int 的指针

    【讨论】:

    • 这会起作用,但不要被愚弄:数组不是指针。它们衰减为指针,也可以用作指针,但它们是一回事:Details here
    猜你喜欢
    • 1970-01-01
    • 2017-11-20
    • 2012-12-19
    • 1970-01-01
    • 2016-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-11
    相关资源
    最近更新 更多