【问题标题】:Understanding pointers in C in terms of strings从字符串的角度理解 C 中的指针
【发布时间】:2014-11-24 12:21:21
【问题描述】:

我刚刚在我的大学开设了一门新课程,它在没有学习所有语法和语义的情况下直接跳到了 C 中(学习起来还不错)。然而,与我理解的语言(java、python)相比,一个很大的区别是指针的概念。

我知道:

& - Address of something
* - value stored at the address of something

所以如果我有这样的字符串:

char a[] = "ABCDEF";

'a' 是否有与之关联的地址 (&a),如果我有 (*a),它是否引用整个字符串? “数组”中的第一个字符(A)?

看到它是一个 char [] 字符串中的每个字符都有自己的地址吗?

我的最终目标是编写一个可以操作指针的函数,以便找到一个字符串中与第二个字符串中的字符匹配的第一个字符。

我在正确的轨道上吗?请注意,这都是伪代码,因为我仍在尝试学习 C 语法

#include <stdio.h>

int main() {
    create address to 'a'
    create address to 'b'
    make 'a' a string like "abcdefg"
    make 'b' a string like 'b'
    call findMatch(a,b);    //pass in both to a function
    return 0;  // I know I have to have this at the end
}

void findMatch(char a, char b){
    Retrieve the pointer to the first character in the 'a' string
    Increment through 'a' to see if it matches the dereferenced b
    If 'a' contains 'b' print its in the string as well as the address of the location in 'a'
}

example run - findMatch("abcdef","f") gives a print statement that says 'f' is in 'abcdef' and the address location of 'f'

我已经阅读了 C 语言中带有字符串函数的内置库,但我想自己操作指针来学习。

谢谢

【问题讨论】:

  • 问题是C 没有字符串。我们只有字符数组,按照惯例,在字符串的最后一个有效字符之后放置一个\0
  • 你基本上必须克服相信存在诸如“字符串”之类的东西——没有。内存中可能有您相信是字符串并且视为字符串的字节,但对于系统而言,它们也可能代表萝卜。所以什么是字符串完全取决于你来跟踪。而且,由于实际上没有什么可以称为“字符串”,char* 指针只是指向 RAM 中某处的指针。是否可以将其视为“字符串”纯粹是您如何处理它的问题。
  • 那么在“abcd”的字符数组中,有没有办法只引用“a”并通过数组递增它?谢谢
  • 我赞赏你决定学习字符串、数组和指针,而不是仅仅用标准容器来铺平它们,但是......你的伪代码几乎是不可理解的,并暗示了对这些事情的一些深刻误解工作。我强烈建议你先玩一会儿int*int[],因为人们似乎对它们没有同样的误解。

标签: c string pointers syntax


【解决方案1】:

考虑一下,在 C 语言中,所有 RAM 都是一个巨大的 char 数组。实际上没有什么(实质性的)将一个概念实体与另一个分开。

您可以在 RAM 中有一个字符串作为字节 H o w . n o w . b r o w n . c o w (zero byte),紧接着字节 0x01 0x02 0x03 0x04 代表我的退休帐户中的余额。如果您将字符串更新为“How now chartreuse cow”,您将在我的退休余额上写上“cow”,我将在贫民窟(如果我已经不在的话)。 (虽然实际上“牛”可能是一种改进。)

RAM 是一个巨大的char 数组,任何类型的指针都只是指向该数组的指针。没有什么将 RAM 中的位置“标记”为一种或另一种类型,并且,鉴于您可以对指针进行任意算术运算,您几乎可以访问任何内容(在您的进程地址空间内)或修改那里的任何内容,无论这样做是否有意义不管怎么样。

【讨论】:

    【解决方案2】:

    尽可能简单地说:

    char* str1 = "Hello world, goodbye world."; char* str2 = *str1;

    您将在str1 指向的位置将char 值分配给指针值str2。由于'H' 在ASCII 中是0x48,所以str2 指向的位置变为0x48。因此,如果您取消引用 str2,您将尝试读取内存中位置 0x48 的一个字节。

    str1 实际上是从内存中的位置0x48 开始的,这是极不可能但可能的,并且有一次可以工作。但是你绝对不应该依赖那个。

    您在这里的意思可能是char* str2 = str1;。在这种情况下,str2 现在指的是str1 在分配时执行的相同位置(因此,如果您更改str1 但保持str2 不变,如果您已删除内存,则在取消引用str2 时可能会遇到问题被指出)。但是它们仍然指向内存中的同一个块,因此更改 str1str2 将分别抛出 str2str1,要么更改 BOTH 指向的块的内容,要么使一个无效,因为内存已移动到其他地方。这取决于正在执行的操作。

    如果你想要一个单独的副本,你必须复制内存块。 size_t len = strlen(str1); str2 = new char [len]; memcpy(str2,str1,len); 然后,您也必须单独删除它们。 delete [] str1; 不会影响str2delete [] str2; 不会影响str1。如果副本被克隆并位于单独的内存块中,它们不会以任何方式连接。

    这个故事的寓意是,您并没有真正将字符串作为实体来处理,而只是在概念上。您正在处理字符数组,就像处理整数数组或指针数组(从技术上讲,它们是整数数组,只是一种特定类型)。

    【讨论】:

    • 实际上,不,编译器会提示您尝试使用char 初始化char *...
    【解决方案3】:

    在 C 中:

    • 指针是地址。您几乎可以互换使用这些术语。
    • 数组变量是指向它所引用的数组的第一个元素(即地址)的指针。
    • 字符串只是一个字符数组(或 wchars 表示宽的非 ascii 字符)。

    当您的代码包含如下声明时:

    char a[] = "ABCDEF";
    

    编译器为整个字符数组分配足够的内存,并在适当的位置(例如堆栈)加上一个尾随 \0 终止符字节,并相应地写入字节。

    记住你的变量a现在是第一个字母'A'的地址;因此,您可以像使用指针一样使用它。例如,使用* 运算符(称为取消引用)访问其值并进行比较:

    *a == 'A' // commonly written as: a[0] == 'A'
    

    将评估为真。因此,以下所有的评估结果也都为真:

    *(a + 1) == 'B' // commonly written as: a[1] == 'B'
    *(a + 2) == 'C' // commonly written as: a[2] == 'C'
    *(a + 3) == 'D' // commonly written as: a[3] == 'D'
    *(a + 4) == 'E' // commonly written as: a[4] == 'E'
    *(a + 5) == 'F' // commonly written as: a[5] == 'F'
    *(a + 6) == '\0' // commonly written as: a[6] == '\0'
    

    如果需要任何其他说明,请告诉我。

    【讨论】:

    • 这真的很有帮助,这是我所希望的。谢谢!如果我在 main() 中声明 'char a[] = "abcd",我应该希望在我的函数中将 *a 作为参数传递,然后传递可以取消引用到我的数组的地址?
    • 如果你想将a 传递给另一个函数,你不应该使用星号。只需使用foo(a) 调用该函数。然后函数foo 将被声明为void foo(char *a) { //code goes here }void foo(char a[]) { //code goes here },它们是相同的(但如果你返回一个值,当然替换void)。
    • 顺便说一句,我真的认为你应该买一本合适的书来彻底了解基础知识。我的推荐:C Programming: A Modern Approach by K. N. King。
    猜你喜欢
    • 2018-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-28
    • 1970-01-01
    • 1970-01-01
    • 2020-11-04
    • 2011-03-08
    相关资源
    最近更新 更多