欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C语言基础学习之指针,数组和内存杂讲

程序员文章站 2022-07-05 23:08:48
c语言基础学习之指针,数组和内存杂讲 大端与小端 sizeof对数组名和普通指针的区别 下标运算符 数组指针和指针数组 c语言允许直接访问物理地址,可以直接对硬件进行操作 因此c既具有高级语言的功能...

c语言基础学习之指针,数组和内存杂讲

大端与小端 sizeof对数组名和普通指针的区别 下标运算符 数组指针和指针数组

c语言允许直接访问物理地址,可以直接对硬件进行操作

因此c既具有高级语言的功能,又具有低级语言的许多功能,能够像汇编语言一样对位、字节和地址进行操作。

我们每个知识点会用具体的例子实现,方便理解。·


1.大端与小端

执行下面的代码,看看输出结果是什么(注意:源文件后缀为.c)

#include 
int main(){
    short num = 0x1122;     //short 占两个字节 
    char *c;                //char 占一个字节 
    c = #
    if(*c == 0x22)
        printf("this is little end\n");
    else
        printf("this is big end\n");
    return 0;
}

C语言基础学习之指针,数组和内存杂讲

这表示我们的计算机处于小端模式,那么什么是大端模式和小端模式呢?

大端:数据的低位保存在高地址中,而高位保存在低地址中。 小端: 数据的低位保存在低地址中,高位保存在高地址中。

从上述的例子中,我们先定义一个short型的变量,我们知道short类型占两个字节,把0x1122赋给这个变量,再定义一个指向字符型的指针变量,把short变量的地址赋给它,最终输出显示 这个字符型指针指向的就是short型变量的低位。我们可以用下图来表示这个过程。

0x1122 11是高位,22是低位,我们让short的地址赋给c,其实就是让c指向short的位置,但因为char类型只有一个字节,所以只会输出short类型中的一个字节,如图所示:

C语言基础学习之指针,数组和内存杂讲

*c输出结果为22,代表目前低位存在低地址中。所以现在机器是小端模式。


2.sizeof的注意项

#include 
int main(){
    int a[3] = {1,2,3}; 
    int *p = a;
    printf("%d\n", sizeof(a));
    printf("%d\n", sizeof(p));
    return 0;
}

输出结果是这样

C语言基础学习之指针,数组和内存杂讲

因为a是数组名,sizeof在这里会自动判断,于是输出这个数组的大小。但虽然指针变量p也指向数组首地址,但sizeof判断接收到的是指针变量而不是数组名时,会输出 当前指针类型的大小。这个数组有三个整形变量,每个变量占四个字节,所以就是大小就是12字节,在64位下,指针变量的大小是8字节(32位下是4字节)。


3.下标运算符

在学完数组之后,我们都知道[]是放数组下标的地方,但怎么用其他方式表示呢?实际上,a[n] 就等同于 * (a+n)。同样我们可以这样写,n[a],这样写好像是错误但,但实际上它表示 *(n+(a)),其实和上面表示的意思是相同的。

下面是个例子

#include 

int main(int argc, char *argv[]){
    int nums[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    printf("%d\n", nums[1][-2]);
    printf("%d\n", (-1)[nums][5]);
    printf("%d\n",  -1[nums][5]);
    return 0;
}
nums[1][-2] 表示 : ((nums+1)+(-2)) 其实就是 nums[0][1] 结果是 2。 (-1)[nums][5] 表示:(*(nums+(-1))+5) 先向前移动一个一维元素大小(数组),再向后移动五个二维元素的大小(整形变量),其实就是nums[0][3],结果是3。 -1[nums][5] 表示:-((1+(nums))+5),其实就是-a[2][2],结果是-9。

4.数组指针和指针数组

我们先来看两行代码:int * a[5]和 int (* a)[5],它们的意义相同吗?答案是否定的。int *a[5] 表示a是一个数组,里面每个数组元素都是指向整形的指针变量。int ( *a)[5],()的优先级高于[],所以首先a是一个指针类型,其次这个指针指向一个数组。

下面是个例子:

#include 
int main(){
    int array[5] = {1, 2, 3, 4, 5};
    printf("%p %p\n", array, &array[0]);
    printf("%p %p\n", &array + 1, &array[0] + 1);
    int (*parray)[5] = &array;          //数组指针 
    for (int i = 0; i < 5; i++) {
        printf("%d ", *parray[i]);
    }
    printf("\n");
    int array2[2][5] = {
        {1, 2, 3, 4, 5},
        {6, 7, 8, 9, 10}
    };
    parray = array2;
    printf("%d %d\n", *(*(array2 + 1) + 1), parray[1][1]);
    int (*parray2)[2][5] = &array2;
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 5; j++) {
            printf("%d ", (*parray2)[i][j]);
        }
        printf("\n");
    }
    return 0;
}

注:%p代表该变量的地址。

首先输出 array的地址 和array[0]的地址,这表明,数组名就表示数组的首地址。

接下来定义一个数组指针指向array,此时给这个指针每进行一次下标运算符,在内存中移动的是整个数组的大小,所以除了本身,其他运算后的数值都是不可预测的。

后面的也是相同的道理,只不过是二维数组的东西,理解以上之后会很容易理解这些。