目录

## 数组概念

## 数组使用

## 数组初始化

## 数组名

## 数组长度

## 数组相关题目

1、找最大值

2、逆置

## 数组和指针

指针加整数的含义

## 指针数组

## 数组名做函数参数

## 函数参数传递数组

1、在函数内部

2. 在函数外部

## 多维数组

使用下标访问

## 字符数组与字符串

## 字符串输入输出

fgets函数

## 字符指针

## 字符串常用库函数

1、strlen

2、strcpy

3、strcat

4、strcmp

## 案例:自定义一个函数my_strlen(),实现的功能和strlen一样


## 数组概念

  • 一种数据结构,用于存储一组具有相同数据类型的数据
  • 每个元素可以通过一个索引(下标)来访问,索引从 0 开始,最大值为数组长度减 1

## 数组使用

#include <stdio.h>

int main() {
    // 定义了一个数组,名字叫a,有10个成员,每个成员都是int类型
    int a[10]; 
    // a[0]…… a[9],没有a[10]
    // 没有a这个变量,a是数组的名字,但不是变量名,它是常量
    a[0] = 0;
    // ……
    a[9] = 9;
    // 数据越界,超出范围,错误
    // a[10] = 10;  // err

    for (int i = 0; i < 10; i++) {
        a[i] = i; // 给数组赋值
    }

    // 遍历数组,并输出每个成员的值
    for (int i = 0; i < 10; i++) {
        printf("%d ", a[i]);
    }
    printf("\n");

    return 0;
}

## 数组初始化

	int a1[5] = { 1, 2, 3, 4, 5}; // 定义一个数组,同时初始化所有成员变量
	int a2[5] = { 1, 2, 3 }; // 初始化前三个成员,后面所有元素都设置为0
	int a3[5] = { 0 }; // 所有的成员都设置为0

	 // []中不定义元素个数,定义时必须初始化
	int a4[] = { 1, 2, 3, 4, 5 }; // 定义了一个数组,有5个成员

## 数组名

  • 数组名是一个地址的常量,代表数组中首元素的地址,即arr == &arr[0]

## 数组长度

#include <stdio.h>

int main() {
    // 定义一个数组,同时初始化所有成员变量
    int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 

    // 数组名是一个地址的常量,代表数组中首元素的地址
    printf("a = %p\n", a);
    printf("&a[0] = %p\n", &a[0]);

    int n = sizeof(a);     // 数组占用内存的大小,10个int类型,10 * 4  = 40
    int n0 = sizeof(a[0]); // 数组第0个元素占用内存大小,第0个元素为int,4
    int num = n / n0;      // 元素个数
    printf("n = %d, n0 = %d, num = %d\n", n, n0, num);

    return 0;
}

## 数组相关题目

1、找最大值
#include <stdio.h>

int main() {
    // 定义一个数组,同时初始化所有成员变量
    int a[] = {1, -2, 3, -4, 5, -6, 7, -8, -9, 10};

    // 假设第0个元素就是最大值
    int temp = a[0];
    for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++) {
        // 如果有元素比临时的最大值大,就交换值
        if (a[i] > temp) {
            temp = a[i];
        }
    }
    printf("数组中最大值为:%d\n", temp);

    return 0;
}
2、逆置
#include <stdio.h>

int main() {
    // 定义一个数组,同时初始化所有成员变量
    int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 

    int i = 0;  // 首元素下标
    int j = sizeof(a) / sizeof(a[0]) - 1;   // 尾元素下标
    int temp;

    while (i < j) {
        // 元素交换值
        temp = a[i];
        a[i] = a[j];
        a[j] = temp;
        // 位置移动
        i++;
        j--;
    }

    for (i = 0; i < sizeof(a) / sizeof(a[0]); i++) {
        printf("%d, ", a[i]);
    }

    return 0;
}

## 数组和指针

  • 数组名字是数组的首元素地址,但它是一个常量
  • *[]效果一样,都是操作指针所指向的内存
#include <stdio.h>

int main() {
    int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    int i = 0;
    int n = sizeof(a) / sizeof(a[0]);

    for (i = 0; i < n; i++) {
        // * 和 [] 效果一样,都是操作指针所指向的内存
        // printf("%d, ", a[i]);
        printf("%d, ", *(a + i)); //a+i参考指针步长
    }
    printf("\n");

    // 定义一个指针变量保存a的地址
    int *p = a; 
    for (i = 0; i < n; i++) {
        // printf("%d, ", p[i]);
        printf("%d, ", *(p + i));
    }
    printf("\n");

    return 0;
}
指针加整数的含义
  • a 是一个指针,假设它指向一个数组的首地址;i 是一个整数。
  • a + i 表示将指针 a 向后移动 i 个单位。这里的“单位”是指指针所指向的数据类型大小。例如:
  • 如果 a 是一个指向 int 类型的指针,那么 a + i 表示将指针 a 向后移动 i * sizeof(int) 字节。
  • 如果 a 是一个指向 char 类型的指针,那么 a + i 表示将指针 a 向后移动 i * sizeof(char) 字节(sizeof(char) 通常为1字节)。

## 指针数组

数组的每一个元素都是指针类型

#include <stdio.h>

int main() {
    // 指针数组
    int *p[3];
    int a = 1;
    int b = 2;
    int c = 3;

    // 指针变量赋值
    p[0] = &a;
    p[1] = &b;
    p[2] = &c;

    for (int i = 0; i < sizeof(p) / sizeof(p[0]); i++) {
        printf("%d, ", *(*(p + i)));
        // printf("%d, ", *(p[i]));
    }
    printf("\n");

    return 0;
}
  1. *(p + i):取出数组p中的元素内容
  2. *(*(p + i)):由于每个元素存储的是地址,所以需要对地址解引用

## 数组名做函数参数

#include <stdio.h>

// 下面3种写法完全等价
// void print_arr(int a[10], int n)
// void print_arr(int a[], int n)
void print_arr(int *a, int n) {
    int i = 0;
    for (i = 0; i < n; i++) {
        printf("%d, ", a[i]);
    }
    printf("\n");
}

int main() {
    int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    int n = sizeof(a) / sizeof(a[0]);

    // 数组名做函数参数
    print_arr(a, n);

    return 0;
}

## 函数参数传递数组

引入 <stdint.h> 头文件:这个头文件定义了一系列标准整数类型,这些类型具有明确的大小和符号属性,确保在不同平台和编译器上具有一致的特性。

#include <stdio.h>
#include <stdint.h>

// 打印数组, 三种写法皆可
//void print_arr(int arr[5], int size)
//void print_arr(int arr[], int size)
void print_arr(int * arr, int size) {
	
	// 对参数arr使用sizeof, 只会得到int指针的大小(固定为8)
	// [警告] 'sizeof' on array function parameter 'arr' will return size of 'int *' [-Wsizeof-array-argument]
	printf("数组字节数:%d\n", sizeof(arr));
	
	// 数组以参数形式传递时,传递过来的是首元素地址, 
    // 数组的长度信息丢失了,sizeof(arr)得到的是指针的大小,需要将数组的个数size从外部传进来
	for (int i = 0; i < size; i++) {
		printf("%d -> %d\n", i, *(arr + i));
	}

}

int main() {

	// 需求:编写一个函数,按格式打印数组
	int arr[] = {10, 20, 30, 40, 50};

	// 在声明函数的代码块,通过sizeof(arr)才能得到数组的字节占用数
	int size = sizeof(arr) / sizeof(arr[0]);
	
	print_arr(arr, size);

	return 0;
}
1、在函数内部
  • 在函数 print_arr 中,参数 arr 是一个指向 int 的指针(int *arr)。因此,sizeof(arr) 实际上是计算指针的大小,而不是数组的大小。
  • 在大多数现代架构(如 x86-64)上,指针的大小通常是 8字节,sizeof(arr) 的结果是 8

2. 在函数外部
  • 在函数外部,arr 是一个数组,数组有 5 个 int 元素
  • 数组大小=5×sizeof(int)=5×4=20字节,sizeof(arr) 的结果是 20

## 多维数组

#include <stdio.h>
#include <stdint.h>

// 二维数组,用于保存多组数据
int main() {
	// 一维数组:元素是数值
	int stu1[] = {18, 180, 100, 99};
	int stu2[] = {19, 170,  98, 99};
	int stu3[] = {20, 160,  90, 92};


	// 二维数组:元素是一维数组的指针
    // stus 是一个指针数组,每个元素指向一个一维数组。
    int * stus_arr[] = {stu1, stu2, stu3};

	// 第一个[]表示包含了几个一维数组
	// 第二个[]表示每个一维数组有几个元素
	int stus[][4] = {
		{18, 180, 100, 99},
		{19, 170,  98, 99},
		{20, 160,  90, 92},
	};

	printf("%d\n", stus[1][2]);
	printf("%d\n", *(stus[1] + 2));

	return 0;
}
  1. 使用下标访问
  • stus[1][2] 表示访问第 2 行第 3 列的元素。
  • 在 C 语言中,数组下标从 0 开始。
  • 因此,stus[1][2] 对应的值是 98。

2、使用指针访问

  • stus[1] 表示第 2 行的首地址。
  • stus[1] + 2 表示第 2 行首地址向后移动 2 个 int 元素的地址,即第 2 行第 3 列的元素的地址。
  • *(stus[1] + 2) 表示取出该地址所指向的值。
  • 因此,*(stus[1] + 2) 对应的值也是 98。

## 字符数组与字符串

  • C语言中没有字符串,可以通过char的数组来替代
  • 数字0(和字符 '\0' 等价)结尾的char数组就是一个字符串,字符串是一种特殊的char的数组
  • 如果char数组没有以数字0结尾,就不是一个字符串,只是普通字符数组
#include <stdio.h>

int main() {
    char c1[] = {'c', ' ', 'p', 'r', 'o', 'g'}; // 普通字符数组
    printf("c1 = %s\n", c1);                    // 有可能乱码,因为没有'\0'结束符

    // 以'\0'('\0'就是数字0)结尾的字符数组是字符串
    char c2[] = {'c', ' ', 'p', 'r', 'o', 'g', '\0'};
    printf("c2 = %s\n", c2);

    // 字符串处理以'\0'(数字0)作为结束符,后面的'h', 'l', 'l', 'e', 'o'不会输出
    char c3[] = {'c', ' ', 'p', 'r', 'o', 'g', '\0', 'h', 'l', 'l', 'e', 'o', '\0'};
    printf("c3 = %s\n", c3);

    // 使用字符串初始化,编译器自动在后面补0,常用
	char c4[] = "c prog";
    printf("c4 = %s\n", c4);

    return 0;
}

## 字符串输入输出

#include <stdio.h>

int main()
{
    char str[100];

    printf("input string1: ");
    // scanf("%s",str) 默认以空格分隔
    // 可以输入空格
    gets(str);
    printf("output: %s\n", str);

    return 0;
}

注意:gets函数存在容易溢出缓冲区的问题,可使用fgets函数

问题:
char buffer[10];
gets(buffer); // 如果用户输入的字符串超过9个字符,就会导致缓冲区溢出
fgets函数
char buffer[10];
fgets(buffer, sizeof(buffer), stdin); // 安全地读取最多9个字符
  • fgets 会从输入流中读取最多 size - 1 个字符(包括换行符),并在末尾添加一个空字符(\0)作为字符串的终止符。

## 字符指针

  • 字符指针可直接赋值为字符串,保存的实际上是字符串的首地址,字符串指针所指向的内存不能修改,指针变量本身可以修改
#include <stdio.h>

int main() {
    char *p = "hello";  // 和 const char *p = 'hello' 等价,有没有const都一样
    // 指针变量所指向的内存不能修改
    // *p = 'a';    // err
    printf("p = %s\n", p);

    // 指针变量可以修改
    p = "world";
    printf("p = %s\n", p);

    return 0;
}

## 字符串常用库函数

1、strlen
#include <string.h>
size_t strlen(const char *s);
功能:计算指定指定字符串s的长度,不包含字符串结束符‘\0’
参数:
	s:字符串首地址
返回值:字符串s的长度,size_t为unsigned int类型,不同平台会不一样
2、strcpy
#include <string.h>
char *strcpy(char *dest, const char *src);
功能:把src所指向的字符串复制到dest所指向的空间中,'\0'也会拷贝过去
参数:
	dest:目的字符串首地址,如果参数dest所指的内存空间不够大,可能会造成缓冲溢出的错误情况
	src:源字符首地址
返回值:
	成功:返回dest字符串的首地址
	失败:NULL

#include <stdio.h>
#include <string.h>

int main() {
    char dest[20] = "123456789";
    char src[] = "hello world";
    strcpy(dest, src);
    printf("%s\n", dest);

    return 0;
}
3、strcat
#include <string.h>
char *strcat(char *dest, const char *src);
功能:将src字符串连接到dest的尾部,‘\0’也会追加过去
参数:
	dest:目的字符串首地址
	src:源字符首地址
返回值:
	成功:返回dest字符串的首地址
	失败:NULL

#include <stdio.h>
#include <string.h>

int main() {
    char str[20] = "123";
    char *src = "hello world";
    strcat(str, src);
    printf("%s\n", str); //结果123hello world

    return 0;
}
4、strcmp
#include <string.h>
int strcmp(const char *s1, const char *s2);
功能:比较 s1 和 s2 的大小,比较的是字符ASCII码大小。
参数:
	s1:字符串1首地址
	s2:字符串2首地址
返回值:
	相等:0
	大于:>0
	小于:<0

## 案例:自定义一个函数my_strlen(),实现的功能和strlen一样

#include <stdio.h>

// 函数定义
int my_strlen(char * temp) {
    // 定义一个累加个数的变量,初始值为0
    int i = 0;
    // 循环遍历每一个字符,如果是'\0'跳出循环
    while (temp[i] != '\0') {
        // 下标累加
        i++;
    }

    return i;
}

int main() {
    char *p = "hello";
    // 函数调用
    int n = my_strlen(p);
    printf("n = %d\n", n);

    return 0;
}
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐