二维数组 字符串 指针

本文介绍了IOS学习的第12天内容,聚焦于二维数组、字符串和指针。对于二维数组,讲解了其内存存储、地址计算及在函数参数中的使用规则。字符串部分解释了其原理和长度计算。对于指针,阐述了指针变量的作用、声明、使用以及野指针的概念。最后提到了指针在函数中传递多个返回值的应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

IOS学习第12天

二维数组

  • 二维数组在内存中的存储

1.二维数组在内存中也是”从高到低申请连续内存的”,一共有行 * 列 * sizeof(单个元素的数据类型)

2.第0行是在”低”字节

  • 二维数组的地址和行列计算

1.二维数组的地址 == 二维数组名 == 二维数组的低地址 == 二维数组中第0行的地址 == 二维数组中第0行第0个元素地址
== 二维数组第0行的低字节地址

2.二维数组的长度以及行列计算

长度:二维数组的总字节数 / 单个元素的字节数

sizeof(二维数组名) / sizeof(单个元素的字节数)

行数:二维数组的总字节数 / 每一行的字节数

sizeof(二维数组名) / sizeof(二维数组名[0])

列数:每一行的字节数 / 单个元素的字节数

sizeof(二维数组名[0]) / sizeof(单个元素的字节数)
  • 二维数组与函数

1.当二维数组作为函数的参数的时候,会丢失这个二维数组的行数和列数

2.当而为驻足作为函数的参数的时候,行数可以省略,要保证实参二维数组的列数必须要和形参二维数组的列数相同

3.写参数的时候,可以先写行数和列数,最后再写二维数组,然后二维数组的列数用参数的列数来决定

void fuZhi(int row,int col,int nums[][col])
{
    //函数体:给nums这个数组中的每一个元素赋值
    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; j++) {
            //nums[i][j]
            nums[i][j] = i*j+10;
        }
        //        printf("\n");
    }
}

字符串

  • 原理

将字符串数据的每1个字符存储到字符数据中,并在后面追加1个’\0’来表示存储完毕

  • 格式
1>最基本的方式:
    char name[10] = {'a','b','c','\0'};


    2>最简单的方式:
    char name[30] = {"abc"};//看成 字符串 "abc" 是四个字符 分别是'a''b''c''\0'
        字符串可见长度:3
        字符串实际长度:4
        数组的长度: 30
        一般来说 如果问 一个字符串的长度是多少?那么就是指的 实际长度

    3>最常用的方式:
    char name[] = "abc";

"字符串可以看成一个字符数组 "abcd"
"但是 字符数组 不一定是字符串
    char name[4];
    name[0] = 'a';
    name[1] = 'b';
    name[2] = 'c';
    name[3] = 'd';

一个小题:
    char name[20] = {'j','a','c','k'}; 可以看成字符串 "jack"
    char name[20] = {'j','a','\0','k'}; 可以看成字符串 "ja"

    char name[10];
    name[0] = 'j';
    name[1] = 'a';
    name[2] = 'c';
    name[3] = 'k';
    不能,因为k后面虽然后字符,但是不一定是'\0'

    char name[4] = {'j','a','c','k'};不能,末尾没有'\0'

    char name[4] = {"jack"}; 不能,末尾的'\0'已经超出了数组范围
    ```
* 使用字符数组存储字符串注意的地方

1.如果没有指定的长度,给数组赋值一个字符串的时候,那么数组的长度是字符串的可见长度+1

2.如果指定的字符数组的长度小于等于了字符串的长度,这个时候就会有问题

3.C语言中一个中文字符占3个字节

* 使用%s输出存储在字符数组中的字符数据

使用%s格式控制符,就可以输出存储在字符数组中的字符串数据

原理:从给定的地址开始,1个字节1个字节的输出,指导遇到'\0'为止

缺点:

1.如果你要打印的字符串中间有'\0',那么只会打印'\0'前面的部分

2.输入的字符串中间不能有空格,如果有空格,只能存储"空格"之前的字符串

* 字符串长度的计算

int len = 0;
char str[] = “aasdladjkwe”

while(str[len] != ‘\0’)
{
len++;
}
printf(“字符串的长度为”)


* 与字符串相关的函数

| 函数名 | 参数 | 作用 |
| -- | -- | -- |
| puts() | const char * | 输出字符串 |
| gets() | char * | 控制台获取输入的字符串 |
|strlen() | const char * | 计算字符串的可见长度 |
| strcmp() | const char *, const char * | 比较两个字符串的大小 |
| strcpy()| char *, const char *| 拷贝字符串 |
| strcat() | char *, const char * | 连接两个字符数组 |

### 指针
* 指针变量的声明

1.什么叫指针变量
一个变量,用来保存另外一个变量的地址

如果一个指针变量p 存储了 另外一个变量num的地址
那么我们称 这个指针变量 指向了 num变量

存储了谁的地址 就指向了谁

2.指针变量有啥用?
可以通过指针变量,间接 访问/修改/获取 它所指向的变量中数据

3.如何声明一个指针变量?**
语法:数据类型 * 指针变量名
int * p1;定义了一个指针变量 变量名叫p1,说明这个p1变量 能存int类型变量的地址

4.不同类型的指针变量,存储对应类型的变量的地址


* 指针变量的初始化

1.什么叫指针变量
一个变量,用来保存另外一个变量的地址

如果一个指针变量p 存储了 另外一个变量num的地址
那么我们称 这个指针变量 指向了 num变量

存储了谁的地址 就指向了谁

2.指针变量有啥用?
可以通过指针变量,间接 访问/修改/获取 它所指向的变量中数据

3.如何声明一个指针变量?**
语法:数据类型 * 指针变量名
int * p1;定义了一个指针变量 变量名叫p1,说明这个p1变量 能存int类型变量的地址

4.不同类型的指针变量,存储对应类型的变量的地址


* 指针变量的使用

1.如何 使用指针变量 操作它所指向的变量的数据

int num = 10;
int *p_num = &num;//这里的*代表 p_num是一个指针变量

通过p_num指针间接操作指针指向的变量
*p_num; //这里的*代表 解析地址 解析出来就是num变量
打印:printf("%d",*p_num);
赋值:*p_num = 200;

* 野指针

.什么是野指针:
乱指的指针,指针变量中存储的值 是一个垃圾值
int num;//垃圾值
int * p_num;//垃圾地址
这个指针就是野指针
定义一个指针 但是没有给他赋初始值,那么这个指针就是野指针

2.定义一个野指针
int *p_num;
*p_num = 50;//非法操作
野指针 如果随便访问 那么会遇到 难以预料问题
“`
* NULL值

1.
    int num = 0;//定义一个变量 如果不知道初始值,给他赋值一个0

    int *p_num = NULL;//如果定义一个变量,不知道初始值是什么,那么我们会给他一个默认值 NULL
    *p_num = 50;//会 100%报错
2.如果1个指针变量的值是NULL值.这个时候通过指针变量去访问指向的变量的时候 100%报错.(没有意义)

结论: 只要是申明了一个指针 不知道初始值
    那么 我们这么干 int *p_num = NULL;
    ```
* 多个指针指向同一个变量
  1. int num = 10;

    int* p1 = #
    int* p2 = p1;

    p1和p2指针都指向了num变量.

2.练习:
int num1 = 10;//num1 的地址 0x1101
int num2 = 20;//num2 的地址 0x1102
int * p_num1 = &num1;// p_num1 里面的值 0x1101

p_num1 = &num2;//p_num1 里面的值 0x1102

*p_num1 = 30;//num2 = 30;

printf("num1 = %d,num2= %d\n",num1,num2);
* 指针作为函数的参数

**指针作为函数的参数是地址传递,在函数内部,如果改变了地址中变量的值,那么调用者中的变量值也会一样改变**

**一个函数具有多个返回值**

1.这个函数返回值有2个 但是一个函数只有一个返回值
2.就会想到指针,在主调函数中定义两个变量
3.把这两个变量的地址 作为参数传递给函数
4.在函数内部求出最大值最小值之后,将它们分别赋值给 这两个地址解析后的变量里
5.那么函数调用完之后,主函数中的变量的值也会跟着改变

“`

<think>嗯,用户想知道如何在C语言中用二维数组存储和操作字符串。首先,我得回忆一下二维数组的基本概念。二维数组在C语言里可以看作是一个数组数组,每个元素本身又是一个数组。对于字符串来说,每个字符串其实就是一个字符数组,所以用二维数组来存储多个字符串挺合适的,比如像多个名字或者单词的列表。 首先,我需要确定如何声明和初始化这样的二维数组。比如,定义一个能存5个字符串数组,每个字符串最多有20个字符。应该是这样声明:char names[5][20];。这里第一个维度是字符串的数量,第二个是每个字符串的最大长度,包括结尾的'\0'。初始化的时候可以用字符串直接赋值,比如char cities[3][20] = {"Beijing", "Shanghai", "Guangzhou"};,但要注意每个初始化的字符串长度不能超过19,因为要留一个位置给空字符。 接下来是输入输出的问题。用户可能需要从键盘输入字符串二维数组中。这时候应该使用scanf或者更安全的fgets函数。比如用scanf("%s", names[i]),但要注意这样无法处理带空格的字符串,所以可能需要用fgets(names[i], 20, stdin),不过fgets会包括换行符,可能需要处理掉。输出的话,可以用循环遍历每个字符串,用printf或者puts输出。 然后是常见的字符串操作,比如比较、连接、排序。比如要按字母顺序排序字符串数组,可以用strcmp函数比较,然后交换整个字符串的位置。这里需要注意的是,交换字符串不能直接赋值,应该用strcpy或者strncpy来复制字符数组的内容。比如使用冒泡排序,外层循环控制轮数,内层循环比较相邻字符串,若顺序不对则交换。 另外,用户可能关心内存布局。二维数组在内存中是连续存储的,每个字符串按行优先排列。比如names[0]指向第一个字符串的首地址,names[1]指向第二个,依此类推。处理的时候要注意不要越界访问,否则会导致缓冲区溢出或者未定义行为。 还需要提醒用户注意字符串的结束符'\0',在操作时确保每个字符串正确终止。比如使用strcpy复制时,目标数组必须有足够的空间,否则可能覆盖其他内存区域。此外,使用strlen获取字符串长度时,返回的是不包括'\0'的长度,所以在操作时要考虑是否需要+1来包含结束符。 最后,可能还要提到动态分配的情况,但用户的问题明确是二维数组,所以暂时不需要涉及指针数组或者动态内存分配。不过可以提一下如果字符串长度差异大,可能用指针数组更高效,但这是进阶内容了。 总结一下,步骤应该是声明二维字符数组,初始化或输入字符串,然后进行各种操作如排序、比较,并注意字符串处理和内存安全的问题。需要给出具体的代码示例,演示如何存储、输入、输出和排序字符串,帮助用户直观理解。</think>### C语言二维数组存储字符串教程 #### 一、二维数组存储字符串原理 二维数组可视为由多个一维字符数组组成的集合,每个一维数组存储一个字符串。例如定义`char str[5][20]`表示存储5个字符串,每个字符串最大长度为19个有效字符(需保留1个位置给结束符`\0`)[^2][^3]。 #### 二、定义与初始化 **1. 直接初始化** ```c char cities[3][20] = { "Beijing", "Shanghai", "Guangzhou" }; ``` **2. 逐个赋值** ```c char names[5][20]; strcpy(names[0], "Alice"); // 必须使用字符串函数赋值[^2] strcpy(names[1], "Bob"); ``` #### 三、输入输出操作 **1. 通过scanf输入** ```c for(int i=0; i<3; i++){ printf("输入第%d个字符串:", i+1); scanf("%19s", cities[i]); // 限制输入长度防止溢出 } ``` **2. 通过fgets输入(含空格处理)** ```c for(int i=0; i<3; i++){ printf("输入含空格的字符串:"); fgets(names[i], 20, stdin); names[i][strcspn(names[i], "\n")] = '\0'; // 去除换行符 } ``` **3. 字符串输出** ```c for(int i=0; i<3; i++){ printf("%d: %s\n", i+1, cities[i]); } ``` #### 四、字符串操作示例 **1. 字符串比较排序** ```c #include <string.h> // 冒泡排序法 for(int i=0; i<2; i++){ for(int j=0; j<2-i; j++){ if(strcmp(cities[j], cities[j+1]) > 0){ char temp[20]; strcpy(temp, cities[j]); strcpy(cities[j], cities[j+1]); strcpy(cities[j+1], temp); } } } ``` **2. 字符串连接** ```c char fullname[2][40] = {0}; strcpy(fullname[0], cities[0]); strcat(fullname[0], " "); // 添加分隔符 strcat(fullname[0], names[0]); // 最终格式如"Beijing Alice" ``` #### 五、内存布局说明 二维数组在内存中按行连续存储,如: $$ \begin{array}{|c|c|c|} \hline B & e & i & j & i & n & g & \backslash0 & ... \\ \hline S & h & a & n & g & h & a & i & \backslash0 & ... \\ \hline ... \\ \hline \end{array} $$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值