C语言数组,函数,以及指针初阶

一,字符数组

  • 概念:专门存放字符的数组,称为字符数组

  • 初始化与元素引用

  • char s1[5] = {'a', 'b', 'c', 'd', 'e'};       // s1存放的是字符序列,非字符串
    char s2[6] = {'a', 'b', 'c', 'd', 'e', '\0'}; // s2存放了一个字符串

    char s[6] = {"abcde"}; // 使用字符串直接初始化字符数组
    char s[6] =  "abcde" ; // 大括号可以省略

    s[0] = 'A'; // 索引第一个元素,赋值为 'A'

1,数组偏移量

int Array[6] = {10,20,30,40,50,60};
// 数组地址偏移量
// 000000000061FE04,000000000061FE04
printf("%p,%p",&Array[1],&Array[0]+1);

2,数组元素地址解引用

通过对数组元素地址解引用,可以获取地址空间里面的数据

    int a = 10;
    printf("%d\n",a);

    printf("%p\n",&a);
    // * 表示将地址里面的内容取出,我们把它称为解引用
    printf("%d\n",*(&a));

    //--------------------------
    char Array[5] = {'j','a','c','k'};
    printf("%c\n",Array[0]);
    printf("%c,%c\n",*(Array+0),*(&Array[0]));
    printf("%c,%c\n",*(Array+1),*(&Array[1]));
    printf("%c,%c\n",*(Array+2),*(&Array[2]));
    printf("%c,%c\n",*(Array+3),*(&Array[3]));
    

3.字符串常量

  • 字符串常量在内存中的存储,实质是一个匿名数组

  • 匿名数组,同样满足数组两种涵义的规定

  • 示例:

printf("%s\n","hello");
printf("'h' addr:%p\n","hello");
printf("'e' addr:%p\n","hello" + 1); //地址+1

printf("'o' addr:%p value:%c\n","hello" + 4,*("hello" + 4)); //地址+1

printf("%d\n", sizeof("abcd")); // 此处 "abcd" 代表整个数组
printf("%p\n", &"abcd");        // 此处 "abcd" 代表整个数组

printf("%c\n", "abcd"[1]); // 此处 "abcd" 代表匿名数组的首元素地址

 

二,多维数组(二维数组)

概念:若数组元素类型也是数组,则该数组称为多维数组,就是一维数组的集合  

int a0[3];
int a1[3];

int a[2][3];//a[0]-->a0 a[1]-->a1

int ;    a[3] // 数组
    
int [3];   a[2] // 数组

第一种解释:
    定义一个二维数组。该数组是由2个一维数组组成,分别是a[0] a[1]
    每个一维数组由3个元素组成,所以二维数组有6个元素
   数据类型 二维数组的名字[有多少个一维数组][每个一维数组有多少个元素]
    // 代码释义:
    // 1, a[2]   是数组的定义,表示该数组拥有两个元素
    // 2, int [3]是元素的类型,表示该数组元素是一个具有三个元素的整型数组

第二种解释:
    该数组一共有2行,每行由3个元素组成(2行3列)
    数据类型 二维数组名[行][列]
    所谓的行:表示这个二维数组一共有多少个一维数组
    所谓的列:表示这个二维数组每个一维数组有多少个元素

初始化:  

int a[2][3] = {{1,2,3}, {4,5,6}}; // 数组的元素是另一个数组

int a[2][3] = {{1,2,3}, {4,5,6}, {7,8,9}}; // 错误,越界了
int a[2][3] = {{1,2,3}, {4,5,6,7}};        // 错误,越界了

int a[ ][3] = {{1,2,3}, {4,5,6}}; // OK,自动根据初始化列表分配数组元素个数
int a[2][3] = {{1,2,3}};          // OK,只初始化数组元素的一部分

 元素引用:

// a[0] 代表第一个元素,这个元素是一个具有 3 个元素的数组:{1,2,3}
// a[1] 代表第二个元素,这个元素也是一个具有 3 个元素的数组:{4,5,6}

printf("%d", a[0][0]); // 输出第一个数组的第一个元素,即1
printf("%d", a[1][2]); // 输出第二个数组的第三个元素,即6

二维数组解引用

// 二维数组初始化字符串
char buf1[2][5] = {"jack","rose"};
printf("%s,%s\n",&buf1[0][0],buf[0]);
printf("%s,%s\n",&buf1[1][0],buf[1]);

// 取二维数组中的某个字符
printf("%c,%c,%c,%c\n",buf1[0][1],*(&buf1[0][1]),*(buf1[0]+1),*(*(buf1+0)+1));
printf("%c,%c,%c,%c\n",buf1[1][2],*(&buf1[1][2]),*(buf1[1]+2),*(*(buf1+1)+2));

数组地址偏移

#include <stdio.h>

int main()
{
    char Names[3][5] = {"jack","rose","ken"};
    // printf("Names[0] %p,%p\n",Names[0],&Names[0][0]);
    // printf("Names[1] %p,%p\n",Names[1],&Names[1][0]);
    // printf("Names[2] %p,%p\n",Names[2],&Names[2][0]);
    
    // Names[0]相当于&Names[0][0]
    printf("Names[0]+1=%p,%p\n",Names[0]+1,&Names[0][1]);
    printf("Names[0][0]:%c,%c\n",Names[0][0],*(Names[0]+0));
    printf("Names[0][2]:%c,%c\n",Names[0][2],*(Names[0]+2));

    printf("&Names[0]+1=%p,%p\n",&Names[0]+1,&Names[1]);
    printf("*(&Names[0]+1)=%c,%c\n",*(&Names[0]+1)[0],*(&Names[1])[0]);
    printf("*(&Names[0]+1)=%c,%c,%c,%c\n",(*(&Names[0]+1))[1],(*(&Names[1]))[1], 
                                          *(*(&Names[0]+1)+1),*(*(Names+1)+1));
    
    printf("%c,%c,%c,%c\n",Names[2][2],*(Names[2]+2),(*(Names+2))[2],*((*(Names+2)+2)));

    return 0;
}


 指针

内存地址

  • 字节:字节是内存的容量单位,英文称为 byte,一个字节有8位,即 1byte = 8bits

  • 地址:系统为了便于区分每一个字节而对它们逐一进行的编号,称为内存地址,简称地址,系统通过对应的内存地址从而定位内存的位置

 

基地址

  • 单字节数据:对于单字节数据而言,其地址就是其字节编号。

  • 多字节数据:对于多字节数据而言,期地址是其所有字节中编号最小的那个,称为基地址。

 

 

 

取址符

  • 每个变量都是一块内存,都可以通过取址符 & 获取其地址

  • 例如:

        int a = 100;
        printf("整型变量 a 的地址是: %p\n", &a);

        char c = 'x';
        printf("字符变量 c 的地址是: %p\n", &c);

        double f = 3.14;
        printf("浮点变量 f 的地址是: %p\n", &f);

 

1,指针基础

  • 指针的概念:

    • 一个专门用来存放内存地址的变量,指针也就是指针变量

  • 地址。比如 &a 是一个地址,也是一个指针,&a 指向变量 a

    • 专门用于存储地址的变量,又称指针变量。

 

格式

  • 类型 *指针变量名

  • 解释:

    • “类型” : 指针变量指向的内存空间存放的数据类型

    • “指向” : 如果我保存了你的地址,那么就说我指向你

    • “*” :定义指针变量的固定格式

// 系统中给a申请了4个字节的内存空间 
    int a = 10;
    printf("a addr:%p\n",&a);
    // 定义一个指针变量用于存放a的地址
    int *p = &a;
    // 第一部分:*p :首先p是一个变量,占用内存8个字节,存放了a的地址
    // 第二部分:int 指的是指针变量所指向的内存空间放了什么类型的数据
    printf("p的值:%p addr : %p\n",p,&p);
    printf("a的值:%d addr : %p\n",a,&a); 

指针的定义:  

// 用于存储 int  型数据的地址,p1 被称为 int  型指针,或称整型指针
int *p1; 
// 用于存储 char 型数据的地址,p2 被称为 char 型指针,或称字符指针
char *p2; 
// 用于存储double型数据的地址,p3 被称为 double 型指针
double *p3;  

指针的赋值:赋给指针的地址,类型需跟指针的类型相匹配。  

int a = 100;
p1 = &a; // 将一个整型地址,赋值给整型指针p1

char c = 'x';
p2 = &c; // 将一个字符地址,赋值给字符指针p2

double f = 3.14;
p3 = &f; // 将一个浮点地址,赋值给浮点指针p3

指针的索引:通过指针,取得其指向的目标  

*p1 = 200; // 将 p1 指向的目标(即a)修改为200,等价于 a = 200;
*p2 = 'y'; // 将 p2 指向的目标(即c)修改为'y',等价于 c = 'y';
*p3 = 6.6; // 将 p3 指向的目标(即f)修改为6.6,等价于 f = 6.6; 

指针的尺寸

  • 指针尺寸指的是指针所占内存的字节数

  • 指针所占内存,取决于地址的长度,而地址的长度则取决于系统寻址范围,即字长

  • 结论:指针尺寸只跟系统的字长有关,跟具体的指针的类型无关

  • 32位系统,指针的大小占用4字节

  • 64位系统,指针的大小占用8字节

// 指针大小
int a = 10;
char b = 'c';
float c = 85.5;

char *p1 = &b;
int *p2 = &a;
float *p3 = &c;
printf("p1 size : %ld\n",sizeof(p1));
printf("p2 size : %ld\n",sizeof(p2));
printf("p3 size : %ld\n",sizeof(p3));

二,指针运算

  • 指针加法意味着地址向上移动若干个目标

  • 指针减法意味着地址向下移动若干个目标

  • 示例:

int  a = 100;
int *p = &a; // 指针 p 指向整型变量 a
char b = 100;

char *ptr = &b;
char *k3 = ptr + 2;

int *k1 = p + 2; // 向上移动 2 个目标(2个int型数据)
int *k2 = p - 3; // 向下移动 3 个目标(3个int型数据)

 

 


 

函数入门

1,函数的定义

  • 函数头:函数对外的公共接口

    1. 函数名称:命名规则与变量一致,一般取与函数实际功能相符合的、顾名思义的名称。

    2. 参数列表:即黑箱的输入数据列表,一个函数可有一个或多个参数,也可以不需要参数。

    3. 返回类型:即黑箱的输入数据类型,一个函数可不返回数据,但最多只能返回一个数据

  • 函数体:函数功能的内部实现

  • 语法说明

 返回类型 函数名称(参数1,参数2,.......)
{
    函数体
    return 返回值;
}

语法汇总:

  1. 当函数的参数列表为void时,表示该函数不需要任何参数。

  2. 当函数的返回类型为void时,表示该函数不返回任何数据。

  3. 关键字return表示退出函数。①若函数头中规定有返回数据类型,则 return 需携带一个类型与之匹配的数据;②若函数头中规定返回类型为 void,则 return 不需携带参数。

 总结

函数名前面为返回值的类型,哪怕此函数没有返回值,也要写上void 
     函数的返回类型要顶格
     函数返回类型与函数名空一格
     函数参数要写(),哪怕里面没有任何内容,如果没有内容最好这样写void func(void)
     函数调用者调用子函数的时候,可以不接收返回值
   注意  

 注意: 大括号表示函数的工作范围,如果离开此范围,数据就不属于此函数管辖
     函数不能在main(){}里面实现,虽然不会报错,如果不调用,这个子函数是不会被执行的
     所有函数的实现都在主函数外面实现

2,实参与形参

  • 概念:

    • 函数调用中的参数,被称为实参,即arguments

    • 函数定义中的参数,被称为形参,即parameters

  • 实参与形参的关系:

    • 实参与形参的类型和参数个数必须一一对应。

    • 形参的值由实参初始化。

    • 形参与实参位于不同的内存区域,彼此独立

// 函数定义中,x、y都属于形参,位于函数 max 的栈内存中
// 它们的值由实参一一对应初始化
int max(int x, int y)
{
    int z;
    z = x>y ? x : y;
    return z;
}

int main(void)
{
    int a = 1;
    int b = 2;
    int m;
        
    // 函数调用中,a、b都属于实参,存储于主函数 main 的栈内存中
    m = max(a, b);    
}

 

3,函数调用的流程

函数调用时,进程的上下文回切换到被调函数,当被调函数执行完毕之后再切换回去。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值