**用C语言做什么

Jam*_*mes 37 c memory arrays pointers

我是C的新手,具有良好的java背景,我正在尝试理解指针和数组.

我知道下标operator[]是数组定义的一部分,所以:

int numbers[] = {1,3,4,5};
Run Code Online (Sandbox Code Playgroud)

会创建一个整数数组,它将在内存中表示为16个字节,4个4个字节:

numbers[0] = 1, address 0061FF1C
numbers[1] = 3, address 0061FF20
numbers[2] = 4, address 0061FF24
numbers[3] = 5, address 0061FF28
Run Code Online (Sandbox Code Playgroud)

但是,当涉及指针时,我的知识开始崩溃,所以如果我要创建一个指向数组数字的指针,我将执行以下操作:

int *pNumbers = &numbers[0];
Run Code Online (Sandbox Code Playgroud)

看起来像这样:

指向数字的指针

而且我猜它的大小是4字节?

然而,**我读作"指针指针"对我来说没有意义,为什么有人想要指针指针,当然如果a-> b-> c然后a-> c就足够了?我知道我错过了一些东西,它必须与数组有关,argv可以是类型char[ ]char **下面看到的:

int main(int argc, char **argv){}
Run Code Online (Sandbox Code Playgroud)

所以:

  • 这是什么(**)?
  • 它有什么用途?
  • 它在记忆中如何表现?

Vla*_*cow 32

在C中,参数由值传递.例如,如果你在main中有一个整数变量

int main( void )
{
    int x = 10;
    //...
Run Code Online (Sandbox Code Playgroud)

以及以下功能

void f( int x )
{
    x = 20;
    printf( "x = %d\n", x );
} 
Run Code Online (Sandbox Code Playgroud)

那么如果你像这样调用main中的函数

f( x );
Run Code Online (Sandbox Code Playgroud)

然后参数获取xmain 中变量的值.但是,参数本身在内存中占用的内容与参数不同.因此,函数中参数的任何更改都不会影响main中的原始变量,因为这些更改发生在不同的内存范围中.

那么如何在函数中更改main中的变量?

您需要使用指针传递对变量的引用.

在这种情况下,函数声明将如下所示

void f( int *px );
Run Code Online (Sandbox Code Playgroud)

而函数定义将是

void f( int *px )
{
    *px = 20;
    printf( "*px = %d\n", *px );
} 
Run Code Online (Sandbox Code Playgroud)

在这种情况下,原始变量占用的内存范围x会发生变化,因为在函数内部我们可以使用指针访问此范围

    *px = 20;
Run Code Online (Sandbox Code Playgroud)

当然,函数必须在main中调用

f( &x );
Run Code Online (Sandbox Code Playgroud)

考虑到px作为指针的参数本身通常是函数的局部变量.也就是说,该函数创建此变量并使用变量的地址对其进行初始化x.

现在让我们假设你在main中声明了一个指针,例如以下方式

int main( void )
{
   int *px = malloc( sizeof( int ) );
   //..
Run Code Online (Sandbox Code Playgroud)

并且函数定义为

void f( int *px )
{
    px = malloc( sizeof( int ) );

    printf( "px = %p\n", px );
}
Run Code Online (Sandbox Code Playgroud)

由于参数px是分配给它的局部变量,因此任何值都不会影响原始指针.该函数更改了与pxmain中原始指针占用的范围不同的内存范围.

如何更改函数中的原始指针?只需通过参考传递它!

例如

f( &px );
//...

void f( int **px )
{
    *px = malloc( sizeof( int ) );

    printf( "*px = %p\n", *px );
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,存储在原始指针中的值将在函数内更改,因为使用解除引用的函数访问定义原始指针的相同内存范围.

  • 那么指向指针(**)的指针存储的是另一个指针的内存地址? (2认同)

kir*_*rig 11

问:这是什么(**)?

答:是的,就是这样.指向指针的指针.

问:它有什么用途?

答:它有很多用途.特别是在表示二维数据(图像等)时.在你的例子的情况下char** argv可以被认为是一个chars 数组的数组.在这种情况下,每个都char*指向一个字符串的开头.实际上你可以自己明确地声明这些数据.

char* myStrings[] = {
    "Hello",
    "World"
};

char** argv = myStrings;

// argv[0] -> "Hello"
// argv[1] -> "World"
Run Code Online (Sandbox Code Playgroud)

当您访问像数组一样的指针时,您索引它的数字和元素本身的大小将用于偏移到数组中下一个元素的地址.您也可以像这样访问所有数字,事实上这基本上就是C正在做的事情.请记住,编译器知道类型int在编译时使用了多少字节.所以它知道每一步应该对下一个元素有多大.

*(numbers + 0) = 1, address 0x0061FF1C
*(numbers + 1) = 3, address 0x0061FF20
*(numbers + 2) = 4, address 0x0061FF24
*(numbers + 3) = 5, address 0x0061FF28
Run Code Online (Sandbox Code Playgroud)

*操作被称为引用操作.它用于从指针指向的内存中检索值.numbers字面上只是指向数组中第一个元素的指针.

在我的例子的情况下myStrings可以看起来像这样假设指针/地址是4个字节,这意味着我们在32位机器上.

myStrings = 0x0061FF14

// these are just 4 byte addresses
(myStrings + 0) -> 0x0061FF14 // 0 bytes from beginning of myStrings
(myStrings + 1) -> 0x0061FF18 // 4 bytes from beginning of myStrings

myStrings[0] -> 0x0061FF1C // de-references myStrings @ 0 returning the address that points to the beginning of 'Hello'
myStrings[1] -> 0x0061FF21 // de-references myStrings @ 1 returning the address that points to the beginning of 'World'

// The address of each letter is 1 char, or 1 byte apart
myStrings[0] + 0 -> 0x0061FF1C  which means... *(myStrings[0] + 0) = 'H'
myStrings[0] + 1 -> 0x0061FF1D  which means... *(myStrings[0] + 1) = 'e'
myStrings[0] + 2 -> 0x0061FF1E  which means... *(myStrings[0] + 2) = 'l'
myStrings[0] + 3 -> 0x0061FF1F  which means... *(myStrings[0] + 3) = 'l'
myStrings[0] + 4 -> 0x0061FF20  which means... *(myStrings[0] + 4) = 'o'
Run Code Online (Sandbox Code Playgroud)


Som*_*ude 8

编写argv参数的传统方法是char *argv[]提供有关它的更多信息,一个指向字符的指针数组(即字符串数组).

但是,当一个数组传递给一个函数时,它会衰减到一个指针,给你一个指向指针的指针char,或者char **.


当然,在取消引用指针的指针时也可以使用双星号,因此在问题结尾没有添加上下文的情况下,对于**C中的含义有两个答案,具体取决于上下文.

要继续该argv示例,获取第一个元素的第一个字符的一种方法argvargv[0][0],或者您可以使用取消引用运算符两次,如**argv.

在大多数地方,数组索引和解除引用是可以互换的,因为对于任何指针数组p和索引i,表达式p[i]都相当于*(p + i).如果i是,0那么我们*(p + 0)可以缩短到*(p)哪个是相同的*p.

作为好奇心,因为p[i]相当于*(p + i)和加法的交换性,表达式*(p + i)等于*(i + p)导致p[i]等于i[p].


最后一个关于过度使用指针的警告,你可能有时会听到三星级程序员的短语,这就是当你使用三个星号时***(比如指向指针的指针).但引用链接

需要明确的是:被称为ThreeStarProgrammer通常不是一种恭维

和另一条警告:数组的数组是一样的指针的指针(链接到矿井,其中还示出了指针的内存布局的指针作为数组的数组的替代品的一个老的答案)


hac*_*cks 5

**in declaration表示指向指针的指针.指针本身就是一种数据类型,就像其他数据类型一样,它可以有一个指针.

int i = 5, j = 6; k = 7;
int *ip1 = &i, *ip2 = &j; 
int **ipp = &ip1;  
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

指向指针的指针在分配动态2D数组时很有用.分配10x10 2D阵列(可能不连续)

int **m = malloc(sizeof(int *)*10;  
for(int i = 0; i < 10; i++)
    m[i] = malloc(sizeof(int)*10  
Run Code Online (Sandbox Code Playgroud)

当您想要通过函数更改指针的值时,也会使用它.

void func (int **p, int n)  
{
    *p = malloc(sizeof(int)*n); // Allocate an array of 10 elements 
}

int main(void)
{
    int *ptr = NULL;
    int n = 10;
    func(&ptr, n);
    if(ptr)
    {
        for(int i = 0; i < n; i++)
        {  
             ptr[i] = ++i;
        }  
    }

    free(ptr);
}
Run Code Online (Sandbox Code Playgroud)

进一步阅读:指向指针.


Joh*_*ebe 5

**代表一个指向指针的指针。如果你想通过引用传递参数,你可以使用*,但如果你想通过引用传递指针本身,那么你需要一个指向该指针的指针,因此**


hqt*_*hqt 5

**如您所知,代表指向指针的指针。我将解释您的每个问题:

这是什么 (**)?

指向指针的指针。有时人们称双指针。例如:

int a = 3;
int* b = &a; // b is pointer. stored address of a
int**b = &b;  // c is pointer to pointer. stored address of b
int***d = &c; // d is pointer to pointer to pointer. stored address of d. You get it. 
Run Code Online (Sandbox Code Playgroud)

它在内存中是如何表示的?

c在上面的例子中只是一个普通变量,与其他变量(指针,int ...)具有相同的表示形式。变量 c 的内存大小与b平台相同。例如,32 位计算机,每个变量地址包括 32 位,因此大小为 4 个字节(8x4=32 位) 在 64 位计算机上,每个变量地址为 64 位,因此大小为 8 个字节(8x8=64 位)。

它有什么用?

指向指针的指针有很多用法,取决于您的情况。例如,这是我在算法课上学到的一个例子。你有一个链表。现在,您要编写一个方法来更改该链表,并且您的方法可能会更改链表的头部。(例如:删除一个值等于 5 的元素,删除头元素,交换,...​​)。所以你有两种情况:

1. 如果你只是传递一个 head element 的指针。也许那个头元素将被删除,这个指针不再有效。

2.如果你传递头元素的指针的指针。如果您的头元素被删除,您不会遇到任何问题,因为指针的指针仍然存在。它只是改变另一个头节点的值。

您可以在此处参考上述示例:指向链表中指针的指针

另一种用法是在二维数组中使用。C 与 Java 不同。C中的二维数组,实际上只是一个连续的内存块。Java中的二维数组是多内存块(取决于您的矩阵行)

希望这有帮助:)