C语言学习笔记--指针和数组的关系

1.数组的本质

(1)数组是一段连续的内存空间

(2)数组的空间大小:sizeof(array_type)*array_size;

(3)数组名可看做指向数组第一个元素的常量指针

(4)数组声明时编译器自动分配一片连续的内存空间 ,而指针声明时只分配了用于容纳地址值的 4 字节空间

2.指针的运算

(1)指针是一种特殊的变量,与整数的运算规则为:

p + n == (unsigned int)p + n * sizeof(*p);

当指针 p 指向一个同类型的数组的元素时,p+1 指向当前元素的下一个元素,p-1 指向上一个元素。

(2)指针之间只支持减法运算且参与减法运算的指针类型必须相同。

p1 - p2 = ((unsigned int)p1 – (unsigned int)p2)/sizeof(type)

①只有当两个指针指向同一个数组中的元素时,指针相减才有意义,其意义为指针所指元 素的下标差。

②当两个指针指向的元素不在同一个数组中时,结果未定义。

指针运算的应用

#include <stdio.h>
//统计元素的个数
#define DIM(a) (sizeof(a) / sizeof(*a))
int main()
{
    char s[]={‘H‘,‘e‘,‘l‘,‘l‘,‘o‘};//栈
    char* pBegin = s;
    char* pEnd = s + DIM(s); //关键点,数组名 + n
    char* p = NULL;
    printf("pBegin = %p\n",pBegin); //第 1 个元素的地址
    printf("pEnd = %p\n",pEnd); //最后 1 个元素的地址
    printf("Size: %d\n",pEnd - pBegin); //5
    //在同一行中打印出:Hello
    for (p = pBegin;p < pEnd; p++)
    {
        printf("%c",*p);
    }
    printf("\n");

    return 0;
}

3.数组的访问方式

(1)以下标的形式访问数组中的元素:如 a[i];

(2)以指针的形式访问数组中的元素:如*(a+i)

(3)下标形式与指针形式的转换:a[n]==*(a+n)==*(n+a)==n[a];

#include <stdio.h>
int main()
{
    int a[5] = {0};
    int* p = a;
    int i = 0;
    for(i=0; i<5; i++)
    {
        p[i] = i + 1; //利用指针给数组元素赋值
    }
    for(i=0; i<5; i++)
    {
        //以指针形式来访问数组元素
        printf("a[%d] = %d\n",i,*(a+i));
    }
    for(i=0; i<5; i++)
    {
        //以下标形式来访问数组元素
        i[a] = i + 10; //比较另类,相当于 a[i] = i + 10
    }
    for(i=0; i<5; i++)
    {
        //通过指针来访问数组元素
        printf("p[%d] = %d\n",i,p[i]);
    }

    return 0;
}

指针和数组不同

//ext.c
//本文件应为.c,而不能是头文件。因为头文件会被直接包含在相应文件中,而.c 文件是分别编译的
int a[5] = {1, 2, 3, 4, 5}; //在该文件中,a 被看作一个数组
#include <stdio.h>
//编译这两个文件:gcc test.c ext.c
int main()
{
    //外部文件中 a 被定义成一个数组。int a[5] = {1, 2, 3, 4, 5};
    extern int a[]; //本文件,如果这样声明,a 仍被看作一个数组
    printf("&a = %p\n", &a); //这里的&a 为整个数组的地址
    printf("a = %p\n",a); //a 为首元素的地址,数值上等于&a
    printf("*a = %d\n",*a); //打印出第 1 个元素,即 1

/*
    extern int* a; //如果这样声明,编译器将 a 当成是一个 int 型的指针来使用。
    printf("&a = %p\n", &a); //会从符号表中查到指针 a 的地址,指向数组
    printf("a = %p\n",a); //从指针 a 处,取一个 int 型的数据,即第 1 个元素,为 1
    printf("*a = %d\n",*a); //试图从地址 0x01 处取出数据,违规内存访问错误。
*/
    return 0;
}

4.a 和&a 的区别

(1)a 为数组首元素的地址

(2)&a 为整个数组的地址,即可将数组看成是一种数据结据。如 int[5];

(3)a 和&a 的区别在在指针运算

a + 1 == (unsigned int)a + sizeof(*a);//a 代表首元素地址,*a 表示第 1 个元素

&a + 1 == (unsigned int)(&a) + sizeof(*&a) == (unsigned int)(&a) + sizeof(a);

指针运算经典问题

#include <stdio.h>

int main(){

    int a[5] = {1, 2, 3, 4, 5};
    int* p1 = (int*)(&a + 1); //指向数组最后面,即第5个元素的后面
    int* p2 = (int*)((int)a + 1);//指向数组的起始地址+1byte偏移处
    int* p3 = (int*)(a + 1);  //指向第2个元素

    printf("p1[-1] = %d\n",p1[-1]);//输出第5个元素

    //数组a在内存中从低地址到高地址的分布如下:(小端模式,低字节放入低地址)
    //01 00 00 00,02 00 00 00,03 00 00 00,04 00 00 00,05 00 00 00
    //p2指向数组起始地址+1字节的偏移处,即01的后面,从这个地方开始读
    //出4个字节,00 00 00 02,根据小端模式规则,该值为0x02 00 00 00,
    printf("p2[0] = 0x%X,p2[0] = %d\n",p2[0],p2[0]);//0x02000000==33554432  

    printf("p3[1] = %d\n",p3[1]); //输出第3个元素,即3

    return 0;
}

5.数组参数

(1)数组作为函数参数时,编译器将其编译为对应的指针。因此,一般情况下,当定义的函数中有数组参数时,需要定义另一个参数来标示数组的大小。

void f(int a[])等价于 void f(int* a);

void f(int a[5])等价于 void f(int* a); //就是一个指针,丢失了数组长度的信息

#include <stdio.h>

void func1(char a[5]) //编译时,a被编译为一个指针,丢失了数组长度的信息
{
    printf("In func1:sizeof(a) = %d\n",sizeof(a));//a退化为指针,4

    *a = ‘a‘;
     /*对指针所指向的内存进行存取(注意数组名也可以这样对内存
        进行访问,如访问第1次元素*a,对i个元素*(a+i);这说明数组名
        在使用上很像指针,但数组名并不是指针,见下面的分析*/

    //再次说明a是指针,而不是数组名
    a = NULL;//编译通过,a是指针类型。(而不是数组名,数组名不能作为左值)
}

void func2(char b[]) //b被编译成一个指针,与数组小大有没有被指定无关。
{
    printf("In func2:sizeof(b) = %d\n",sizeof(b));//b退化为指针,仍为4

    *b = ‘b‘; 

    b = NULL;//编译通过,b是指针类型
}

int main(){

    char array[10] = {0};

    func1(array);
    printf("array[0] = %c\n",array[0]); //array[0] = a;

    func2(array);
    printf("array[0] = %c\n",array[0]); //array[0] = b;

    return 0;
}
时间: 2024-10-26 20:38:51

C语言学习笔记--指针和数组的关系的相关文章

C语言学习笔记--指针阅读技巧

1. 指针阅读技巧:右左法则 (1)从最里层的圆括号中未定义的标示符看起 (2)首先往右看,再往左看 (3)遇到圆括号或方括号时可以确定部分类型,并调转方向 (4)重复 2.3 步骤,直到阅读结束 注意: 当读出是数组时,须读出元素个数.元素类型 当读出是函数是,须读出参数及类型,返回值类型 当读出是指针是,须读出指针所指向的类型,有时也须读出指针的类型. ① int (*p)(int*, int (*f)(int*)); A.读未标示符 f,向右遇”)”括号,调转向左遇*,所以 f 是个指针,

C语言学习笔记:18_数组-二维数组

/* * 18_数组-二维数组.c * * Created on: 2015年7月6日 * Author: zhong */ #include <stdio.h> #include <stdlib.h> /** * 二维数组: * 为什么要用二维数组呢; * 例如: 网络班有2个班,每班有5人. * 要想存储一班的学生的年龄 定义一个一维数组搞定 int ages[5]={18,19,20,18,19}; * 然后将两个班中的所有年龄分开存入一个数组中 int classes[2]

C语言学习笔记:19_数组-字符数组与字符串(常用字符串函数)

/* * 19_数组-字符数组与字符串.c * * Created on: 2015年7月7日 * Author: zhong */ #include <stdio.h> #include <stdlib.h> #include <string.h> /** *一:字符数组:就是存放字符的char[]数组 * 由于c语言中没有像java,C#中的String(字符串),只有存放字符 的字符型数组当字符串使用(java中的String类也是对字符数组进行封闭的). * *

C语言学习笔记--指针概念

指针也是一种变量,占有内存空间,用来保存内存地址,在32位系统中指针的占用的内存大小为4个字节 1.*号的意义 (1)在指针声明时,*号表示所声明的变量为指针 (2)在指针使用时,*号表示取指针所指向的内存空间中的值. ①*号类似一把钥匙,通过这把钥匙可以打开内存,读取内存中的值. ②变量 p 保存着变量 i 的内存地址,即 p==&i, *p==i #include <stdio.h> int main() { int i = 0; int* pI; char* pC; float*

【C语言学习】指针再学习(二)之数组的指针

★一维数组 一个整型数据为4个字节.4个字节就是32位,即可以表示2^32个数字 在程序中定义一个数组a[5] = {1,2,3,4,5}; 那么a[0]的地址就是0x00000000,数组名a是数组首元素的地址,a的地址也是0x00000000.a+1则表示的地址是0x00000004,而不是0x00000001.因为1这个值,根据前面的指针a所指向的类型的长度来调整自己的长度.也就是说如果a是指向整型的指针,那么后面加的1也表示4个字节,如果a是指向字符型的指针,那么后面加的1表示1个字节.

C语言学习笔记(五) 数组

数组 数组的出现就是为了解决大量同类型数据的存储和使用的问题: 数组的分类:一维数组.二维数组. 一维数组:为多个变量连续分配存储控件:所有的变量的数据类型必须相同:所有变量所占的字节大小必须相等: 例如:int a[4]; 一维数组名不代表数组中的所有元素而是代表数组中第一个元素的地址: 数组的初始化: 完全初始化: int a[4] = {1,2,3,4}; 不完全初始化: int a[4] = {1};  未被初始化的元素的值默认为0; 数组清零 int a[4] = {0}; 不初始化:

R语言学习笔记

參考:W.N. Venables, D.M. Smith and the R DCT: Introduction to R -- Notes on R: A Programming Environment for Data Analysis and Graphics,2003. http://bayes.math.montana.edu/Rweb/Rnotes/R.html 前言:关于R 在R的官方教程里是这么给R下注解的:一个数据分析和图形显示的程序设计环境(A system for data

go语言学习笔记

go语言学习笔记 go语言学习笔记(初级) 最近一直在学习go语言,因此打算学习的时候能够记录 一下笔记.我这个人之前是从来没有记录笔记的习惯, 一直以来都是靠强大的记忆力去把一些要点记住. 读书的时候因为一直都是有一个很安静和很专心的环境, 因此很多事情都能记得很清楚,思考的很透彻.但是随着 年纪不断增加,也算是经历了很多的事情,加上工作有时会让人 特别烦闷,很难把心好好静下来去学习,去思考大自然的终极 奥秘,因此需要记录一些东西,这些东西一方面可以作为一种自我激励 的机制,另一方面,也算是自

Go语言学习笔记(四) [array、slices、map]

日期:2014年7月22日 一.array[数组] 1.定义:array 由 [n]<type> 定义,n 标示 array 的长度,而 <type> 标示希望存储的内容的类型. 例如: var arr[10] int arr[0] = 1 arr[1] = 2 数组值类型的:将一个数组赋值给 另一个数组,会复制所有的元素.另外,当向函数内传递一个数组的时候,它将获得一个数组的副本,而不是数组的指针. 2.数组的复合声明.a :=[3]int{1,2,3}或简写为a:=[...]i