C语言指针的精通之路------101

一、什么是指针

  • 指针表示内存地址(如酒店的房间号表示房间的地址)
  •  我们平时说的指针,是保存地址的指针变量

二、定义指针变量

【1】格式

数据类型 *指针变量名;
*前面如果有数据类型,表示定义指针变量

#include <stdio.h>
int main(int argc, const char *argv[])
{
    int a,b,c;
    printf("%p\t%p\t%p\n",&a,&b,&c);

    //使用指针变量保存a的地址
    int *p = &a;
    printf("p=%p\n",p); 
    //因为指针变量p的值就是内存地址,所以使用%p格式符打印
    return 0;
}

【2】指针的初始化和赋值

        指针和变量的关系

        指针指向一个变量实际上是指向该变量的首地址

#include <stdio.h>
int main(int argc, const char *argv[])
{
    int *p;     //定义了一个能够保存一个int类型变量地址的指针p
    //上面的p现在是一个野指针(不知道指向的指针)
    
    int a=90;
    //用int类型变量a的地址给指针p赋值
    //让指针p指向变量a
    p = &a;
    /*******************通过指针访问变量的值***********************/
    //拿到变量的地址后,需要对地址解引用访问到地址中的内容
    //可以使用(*地址)解引用
    printf("%d\n",*p);
    *p = 12;   //<==>a=12
    printf("a=%d\n",a);
    //定义指针p1并初始化
    int *p1 = NULL;    //NULL表示空地址,当指针没有明确指向时可以指向NULL
    return 0;
}

【3】通过指针的间接访问

  • 直接通过变量名访问变量是直接访问
  • 通过变量的地址(指针)访问变量是间接访问
  • 如何通过指针访问变量:对地址进行解引用(对地址取*)
  • *p;char *p1;
总结*的用法:
1、定义时,表示定义了指针变量
2、解引用时,表示取地址中的内容

如果*前面有数据类型一定是定义指针变量,
如果*前面没有数据类型一定是对地址的解引用
#include <stdio.h>
int main(int argc, const char *argv[])
{
    int *p;     //定义了一个能够保存一个int类型变量地址的指针p
    //上面的p现在是一个野指针(不知道指向的指针)
    
    int a=90;
    //用int类型变量a的地址给指针p赋值
    //让指针p指向变量a
 p = &a;
 //定义指针p1并初始化
    int *p1 = NULL;    //NULL表示空地址,当指针没有明确指向时可以指向NULL
 
    /*******************通过指针访问变量的值***********************/
    //拿到变量的地址后,需要对地址解引用访问到地址中的内容
    //可以使用(*地址)解引用
    printf("%d\n",*p);
    *p = 12;   //<==>a=12
    printf("a=%d\n",a);

    return 0;
}

【4】指针的运算

  • 因为指针是一个内存地址,对于指针的大部分运算没有实际意义。
  • 指针常用的操作是加法和减法
#include <stdio.h>
int main(int argc, const char *argv[])
{
    int *p;     //定义了一个能够保存一个int类型变量地址的指针p
    //上面的p现在是一个野指针(不知道指向的指针)
    
    int a=90;
    //用int类型变量a的地址给指针p赋值
    //让指针p指向变量a
 p = &a;
    printf("p=%p\n",p);  //0x3c
    printf("p+1=%p\n",p+1); //0x40,相差四个字节 p+1表示p所指向空间的下一个位置
    printf("++p=%p\n",++p); //++p表示指针变量p本身向后偏移一个字节


    char c;
    char *p1=&c;
    printf("p1=%p\n",p1);  //0x73
    printf("p1+1=%p\n",p1+1); //0x74,相差四个字节

    return 0;
}

        指针的加减运算,表示向后或者向前偏移n个字节。

        n由指针的数据类型决定。

【5】指针的大小

  • 指针的大小和数据类型无关,指针的数据类型只决定指针的偏移量
  • 指针的大小由操作系统决定,32位操作系统占4Byte,64位操作系统占8Byte。

【6】指针和一维整形数组

  • 数组的特点:数组名表示数组的首地址
  • 所以指针指向一维数组,可以直接将数组名赋值给指针

        [i]对地址先偏移i个字节,再解引用(取*)

#include <stdio.h>
int main(int argc, const char *argv[])
{
    // 定义一个包含 5 个元素的一维整型数组,并初始化元素值
    int arr[5]={12,8,59,61,70};
    // 定义指针 p 指向数组 arr 的首地址
    int *p=arr;
    // 输出数组 arr 的首地址
    printf("%p\n",arr);
    // 输出数组 arr 偏移 3 个元素后的地址
    printf("%p\n",arr+3);
    // 注释掉的代码,若执行会导致越界访问,因为数组只有 5 个元素
    //printf("%p\n",p+5);   
    // 输出指针 p 偏移 1 个元素后对应的值,即 arr[1]
    printf("%d\n",*(p+1)); 
    // 先取指针 p 所指向的值(即 arr[0]),再加上 2 后输出
    printf("%d\n",*p+2);   
    // p[3] 等价于 *(p + 3),输出数组中第 4 个元素的值
    printf("%d\n",p[3]);   
    return 0;
}

【7】指针和一维整形数组的关系及运算

#include <stdio.h>

// 主函数入口
int main(int argc, const char *argv[])
{
    // 定义一个包含4个整数的数组arr
    int arr[4] = {1, 9, 34, 2};
    
    // 定义一个整型指针p,并将其指向数组arr的首地址
    // 数组名arr本身就是数组首元素的地址
    int *p = arr;  
    // 也可以写成 p = &arr[0]; 两者效果相同

    // 输出指针p指向的数组元素,即arr[0]
    printf("%d", *p);  // *p 等同于 arr[0]

    /*
     * 注释说明指针p与数组arr的关系及运算:
     * p 可以直接当作数组名arr来使用,表示数组的首地址
     * arr[i] 等价于 *(p + i),即通过指针偏移i个元素来访问数组元素
     * p[i] 是 *(p + i) 的简写形式,也等同于 arr[i]
     * *(p + i) 和 *(arr + i) 是等价的,都表示访问数组中偏移i个元素后的值
     * p + i 和 arr + i 都表示指向数组中第i个元素的地址
     * [i]操作符可以理解为先对指针进行i个元素的偏移,然后解引用获取该位置的值
    */

    // 输出指针p偏移1个元素后的地址及该地址处的值(即arr[1])
    printf("p+1=%p\t*(p+1)=%d
", p + 1, *(p + 1)); // 等同于 arr[1]

    // 输出数组名arr偏移2个元素后的地址及该地址处的值(即arr[2])
    printf("arr+2=%p\t*(arr+2)=%d
", arr + 2, *(arr + 2)); // 等同于 arr[2]

    // 输出指针p偏移3个元素后的地址及该地址处的值(即arr[3])
    printf("p+3=%p\t*(p+3)=%d
", p + 3, *(p + 3)); // 等同于 arr[3]

    return 0;
}

测试题:

  • 用指针的形式,完成对数组中元素的输入和输出
#include <stdio.h>

int main(int argc, const char *argv[])
{
    // 定义一个包含 5 个整数的数组 arr
    int arr[5];
    // 定义一个整型指针 p,并将其指向数组 arr 的首地址
    // 这样指针 p 就可以用来操作数组 arr 中的元素
    int *p = arr;  
    // 定义一个整型变量 i,用于循环计数
    int i;

    // 第一个 for 循环,用于从标准输入读取 5 个整数到数组 arr 中
    // sizeof(arr) 表示数组 arr 占用的总字节数
    // sizeof(arr[0]) 表示数组中一个元素占用的字节数
    // sizeof(arr)/sizeof(arr[0]) 计算出数组 arr 的元素个数,这里为 5
    for(i = 0; i < sizeof(arr)/sizeof(arr[0]); i++)
    {
        // 使用 scanf 函数从标准输入读取一个整数
        // p + i 表示数组 arr 中第 i 个元素的地址
        // 因为 scanf 需要传入变量的地址来存储输入的值
        scanf("%d", p + i);
        // 注释说明数组下标和指针偏移的等价关系
        // [i] 操作符本质上等同于 *(指针 + i),即先偏移再解引用
        // &*(p + i) 中,& 和 * 是互逆操作,会相互抵消,所以 &*(p + i) 就等同于 p + i
        //[i]<==>*(+i)
        //&*(p+i)  &和*互相抵消
    }

    // 第二个 for 循环,用于将数组 arr 中的元素依次输出到标准输出
    for(i = 0; i < sizeof(arr)/sizeof(arr[0]); i++)
    {
        // 使用 printf 函数输出数组元素的值
        // *(p + i) 表示访问指针 p 偏移 i 个位置后所指向的元素的值
        // \t 是制表符,用于在输出中添加间隔,使输出更整齐
        printf("%d\t", *(p + i));
    }

    // 程序正常结束,返回 0 表示成功
    return 0;
}
  • 通过指针,求数组中元素的最大值
#include <stdio.h>

// 主函数,程序的入口点
int main(int argc, const char *argv[])
{
    // 定义一个包含 4 个整数的数组 arr,并进行初始化
    int arr[4] = {12, 50, 39, 71};
    // 初始化变量 max 为数组的第一个元素,用于存储数组中的最大值
    int max = arr[0];
    // 定义一个整型指针 p,将其指向数组 arr 的首地址
    // 这样指针 p 就可以用来遍历数组 arr 中的元素
    int *p = arr; 
    // 注释说明指针 p 和数组名 arr 是等价的,都代表数组的首地址
    //p<==>arr
    // 定义一个整型变量 i,用于循环计数
    int i;

    // 使用 for 循环遍历数组中的每一个元素
    for(i = 0; i < 4; i++)
    {
        // 比较当前最大值 max 和指针 p 偏移 i 个位置后所指向的元素的值
        // *(p + i) 表示访问数组中第 i 个元素的值
        if(max < *(p + i))
        {
            // 如果当前元素的值大于 max,则更新 max 的值为该元素的值
            max = *(p + i);
        }
    }

    // 输出数组中的最大值
    printf("最大值为%d\n", max);

    // 程序正常结束,返回 0 表示成功
    return 0;
}
  • 定义一个数组,用指针指向该数组并用指针的形式完成冒泡排序。
#include <stdio.h>

int main(int argc, const char *argv[])
{
    // 定义一个包含 7 个整数的数组 arr 并初始化
    int arr[7] = {12, 71, 28, 34, 59, 20, 7};
    // 定义一个整型指针 p,使其指向数组 arr 的首地址
    // 后续可通过指针 p 来访问数组 arr 中的元素
    int *p = arr;  
    // 定义循环控制变量 i 和 j,以及用于交换元素值的临时变量 temp
    int i, j, temp;
    // 计算数组 arr 的长度
    // sizeof(arr) 得到数组占用的总字节数,sizeof(arr[0]) 得到数组单个元素占用的字节数
    // 两者相除得到数组元素的个数
    int len = sizeof(arr) / sizeof(arr[0]);

    // 外层循环,控制冒泡排序的轮数,总共需要进行 len - 1 轮
    for (i = 1; i < len; i++)
    {
        // 内层循环,每一轮比较相邻元素并交换位置
        // 每一轮比较的次数会随着轮数增加而减少,因为每一轮都会将最大的元素放到正确位置
        for (j = 0; j < len - i; j++)
        {
            // 通过指针对地址解引用的方式访问数组中元素
            // 比较相邻两个元素的大小,如果前一个元素大于后一个元素
            if (*(p + j) > *(p + j + 1))
            {
                // 交换两个元素的值
                // 先将前一个元素的值保存到临时变量 temp 中
                temp = *(p + j);
                // 把后一个元素的值赋给前一个元素
                *(p + j) = *(p + j + 1);
                // 再把临时变量 temp 中保存的值赋给后一个元素
                *(p + j + 1) = temp;
            }
        }
    }

    // 遍历排序好的数组并输出每个元素
    for (i = 0; i < len; i++)
    {
        // 通过指针偏移和解引用的方式访问数组元素并输出
        printf("%d\n", *(p + i));
    }

    // 程序正常结束,返回 0
    return 0;
}
  • 使用指针,实现数组的逆置
/*
*代码实现了数组的反转。通过指针运算和双指针法(一个指向开头,一个指向末尾),逐步交换对应位置的*元素,直到两个指针相遇或交错,从而完成数组的反转。
*/

#include <stdio.h>

int main(int argc, const char *argv[])
{
    // 定义一个包含7个整数的数组arr
    int arr[7] = {12, 71, 28, 34, 59, 20, 7};
    // 定义一个整型指针p,并将其指向数组arr的首地址
    int *p = arr;  // 指针p指向数组arr
    int i, j, temp;
    // 计算数组的长度
    int len = sizeof(arr) / sizeof(arr[0]);

    // 初始化两个指针,i指向数组开头,j指向数组末尾
    i = 0;
    j = len - 1;

    // 当i小于j时,进行循环交换元素
    while (i < j)
    {
        // 保存p指向的当前i位置的值
        temp = *(p + i);
        // 将j位置的值赋给i位置
        *(p + i) = *(p + j);
        // 将保存的i位置的值赋给j位置
        *(p + j) = temp;
        // i向后移动一位
        i++;
        // j向前移动一位
        j--;
    }

    // 遍历并打印反转后的数组元素
    for (i = 0; i < len; i++)
    {
        printf("%d", *(p + i));
    }

    return 0;
}
/*
*同样实现了数组的反转。与方法一不同的是,这里直接使用了指针i和j来进行操作,更加直观地体现了指针*的移动和元素的交换。通过不断移动指针并交换对应位置的元素,最终实现数组的反转。

*这两种方法都利用了指针的特性来操作数组,提高了代码的灵活性和效率。您可以根据实际需求选择适合的方法
*/
#include <stdio.h>

int main(int argc, const char *argv[])
{
    // 定义一个包含7个整数的数组arr
    int arr[7] = {12, 71, 28, 34, 59, 20, 7};
    // 定义一个整型指针p,并将其指向数组arr的首地址
    int *p = arr;  // 指针p指向数组arr
    // 定义指针i指向数组的第一个元素
    int *i = arr;
    // 计算数组的长度
    int len = sizeof(arr) / sizeof(arr[0]);
    int temp;
    // 定义指针j指向数组的最后一个元素
    int *j = arr + len - 1;

    // 当i指针小于j指针时,进行循环交换元素
    while (i < j)
    {
        // 交换i和j指向的元素的值
        temp = *i;
        *i = *j;
        *j = temp;
        // i指针向后移动一位
        i++;
        // j指针向前移动一位
        j--;
    }

    return 0;
}

【8】指针和一维字符数组

#include <stdio.h>

int main(int argc, const char *argv[])
{
    // 定义一个字符数组str并初始化为"hello",剩余部分自动填充为空字符'\0'
    char str[100] = "hello";
    // 定义一个字符指针p,并将其指向字符数组str的首地址
    char *p = str;

    /*
     * 注释及解析:
     * 对于字符数组str和字符指针p,以下关系成立:
     * str[i] <==> p[i]
     * 这是因为str作为数组名,在表达式中会被解释为指向数组首元素的指针,
     * 所以str[i]和p[i]都是访问数组中第i个元素的方式。

     * *(str + i) <==> *(p + i)
     * 这里利用了指针算术,str + i表示从str指向的地址开始偏移i个字符的位置,
     * *(str + i)就是解引用该地址得到第i个字符;同理对于p + i和*(p + i)。

     * str + i <==> p + i
     * str和p都表示数组首元素的地址,所以str + i和p + i都表示指向数组中第i个元素的地址。
     * 这些地址在数值上是相等的,只是类型(指向字符的指针)相同。
     */

    // 下面是一些示例用法:
    printf("%c", str[1]);   // 输出'h',等同于p[1]或*(str + 1)或*(p + 1)
    printf("%c", *(str + 4)); // 输出'o',等同于*(p + 4)

    return 0;
}

/*
在C语言中,数组名在大多数表达式中会被解释为指向数组首元素的指针。因此,对于一维字符数组str和字符指针p(指向str的首地址),它们在很多情况下可以互换使用。
str[i]和p[i]都是访问数组中第i个元素的合法方式。
(str + i)和*(p + i)通过指针算术实现了相同的功能,即访问数组中第i个元素。
str + i和p + i都表示指向数组中第i个元素的地址。
这些关系本质上与指针指向一维整型数组时的一致,只是数据类型从整型变成了字符型。理解这些基本概念对于掌握C语言中的指针和数组操作至关重要。
*/
#include <stdio.h>

int main(int argc, const char *argv[])
{
    // 定义一个字符数组str,并初始化为字符串"hello"
    // 剩余的95个字符会自动初始化为空字符'\0'
    char str[100] = "hello";
    
    // 定义一个字符指针p,并将其指向字符数组str的首地址
    char *p = str;

    // 打印数组名str的值,即数组首元素的地址
    // 在C语言中,数组名在大多数表达式中会被解释为指向数组首元素的指针
    printf("str=%p", str);

    // 打印数组名str偏移1个字符后的地址
    // 即指向数组中第二个元素的地址
    printf("str+1=%p", str + 1);

    // 打印指针p的值,它指向数组str的首地址,与str相同
    printf("p=%p", p);

    // 打印指针p偏移1个字符后的地址
    // 即指向数组中第二个元素的地址,与str+1相同
    printf("p+1=%p", p + 1);

    return 0;
}
/*
需要了解及注意

1. 数组名与指针的关系:

数组名在大多数表达式中会被解释为指向数组首元素的指针。因此,str和p在这里都表示指向字符数组str首元素的地址。

2. 指针算术:

str + 1和p + 1都表示指向数组中第二个元素的地址。因为str和p都是指向字符的指针,所以+1操作会使指针偏移一个字符的大小(通常是1字节)。

3. 打印地址:

使用%p格式说明符来打印指针的值(即地址)。注意,输出的地址值会根据程序的运行环境和编译器而有所不同。

4. 数组初始化:

char str[100] = "hello";这行代码不仅初始化了数组的前6个字符(包括结尾的空字符\0),还自动将剩余的95个字符初始化为空字符\0。
/

测试题:

  • 终端输入字符串,如果有大写字母转为小写字母,如果是小写字母转为大写字母,其他字符转为#

 解题思路

1.字符串输入

  • 使用gets(str);读取用户输入的一行字符串到str数组中。
  • 注意:gets函数是不安全的,因为它不检查缓冲区溢出。建议使用fgets函数代替,如注释中所示。

2.字符串遍历与处理

  • 使用while (*(str + i))循环遍历字符串,直到遇到空字符\0
  • 在循环中,通过指针算术*(str + i)访问字符串中的每个字符。

3.字符转换

  • 如果当前字符是大写字母(ASCII码在65到90之间),通过加上32将其转换为小写字母。
  • 如果当前字符是小写字母(ASCII码在97到122之间),通过减去32将其转换为大写字母。
  • 如果当前字符既不是大写字母也不是小写字母,将其替换为#字符。

4.字符串输出

  • 使用puts(str);输出处理后的字符串。
#include <stdio.h>

int main(int argc, const char *argv[])
{
    // 定义一个字符数组str,用于存储输入的字符串
    char str[100];

    // 使用gets函数读取一行输入到str中
    // 注意:gets函数是不安全的,因为它不检查缓冲区溢出,建议使用fgets代替
    gets(str);
    // 更安全的替代方案:
    // fgets(str, sizeof(str), stdin);

    // int i = 0; // 这行代码被注释掉了,但它在原代码中用于遍历字符串

    // 使用while循环遍历字符串,直到遇到空字符'\0'
    while (*(str + i))  // 如果没有走到'\0'继续循环
    {
        // 如果当前字符是大写字母(ASCII码在65到90之间)
        if (*(str + i) >= 'A' && *(str + i) <= 'Z')
        {
            // 将其转换为小写字母,方法是加上32(大小写字母间的ASCII码差值)
            *(str + i) += 32;
        }
        // 如果当前字符是小写字母(ASCII码在97到122之间)
        else if (*(str + i) >= 'a' && *(str + i) <= 'z')
        {
            // 将其转换为大写字母,方法是减去32
            *(str + i) -= 32;
        }
        // 如果当前字符既不是大写字母也不是小写字母
        else
        {
            // 将其替换为'#'字符
            *(str + i) = '#';
        }
        // 移动到下一个字符
        i++;
    }

    // 使用puts函数输出处理后的字符串
    puts(str);

    return 0;
}
  • 使用指针实现字符串函数族的函数:strlen、strcpy、strcat、strcmp

看好了,开始实现了呦

#include <stdio.h>

// 自定义 strlen 函数,计算字符串长度
size_t my_strlen(const char *str) 
{
    const char *p = str;
    while (*p) 
{
        p++;
    }
    return p - str;
}

// 自定义 strcpy 函数,复制字符串
char *my_strcpy(char *dest, const char *src) 
{
    char *p = dest;
    while (*src) 
{
        *p = *src;
        p++;
        src++;
    }
    *p = '\0';
    return dest;
}

// 自定义 strcat 函数,拼接字符串
char *my_strcat(char *dest, const char *src) 
{
    char *p = dest;
    // 找到目标字符串的结束符
    while (*p) 
    {
        p++;
    }
    // 拼接源字符串
    while (*src) 
    {
        *p = *src;
        p++;
        src++;
    }
    *p = '\0';
    return dest;
}

// 自定义 strcmp 函数,比较字符串
int my_strcmp(const char *str1, const char *str2) 
{
    while (*str1 == *str2 && *str1) {
        str1++;
        str2++;
    }
    return *str1 - *str2;
}

int main(int argc, const char *argv[]) 
{
    char str[100] = "hello";
    char str1[20] = " world";

    // 测试 strlen 功能
    size_t len = my_strlen(str);
    printf("字符串 str 的长度为: %zu\n", len);

    // 测试 strcpy 功能
    char copy_str[100];
    my_strcpy(copy_str, str);
    printf("复制后的字符串为: %s\n", copy_str);

    // 测试 strcat 功能
    char cat_str[100];
    my_strcpy(cat_str, str);
    my_strcat(cat_str, str1);
    printf("拼接后的字符串为: %s\n", cat_str);

    // 测试 strcmp 功能
    int ret = my_strcmp(str, str1);
    printf("字符串比较结果为: %d\n", ret);

    return 0;
}
  • 指针指向字符串常量区的内容,不能通过指针修改
#include <stdio.h>

int main(int argc, const char *argv[])
{
    // 定义一个字符指针p,并将其指向一个字符串常量"hello world"
    char *p = "hello world";
    // 注释解释:
    // 字符串常量"hello world"被存储在只读数据段(.rodata段)中
    // 该段的内容在程序运行期间不能被修改

    // 尝试修改字符串常量的第一个字符为'a'
    // *p = 'a'; // 这行代码会导致未定义行为,因为试图修改只读内存
    // 如果取消注释这行代码,程序可能会崩溃或产生其他不可预测的行为

    printf("1"); // 输出数字1

    p = NULL; // 将指针p设置为NULL,表示它不指向任何有效的内存地址

    // 尝试解引用NULL指针并打印其值
    // 这将导致段错误(Segmentation Fault),因为NULL指针不指向任何有效的内存
    printf("%c", *p); // 段错误

    return 0;
}

【9】段错误 

        本质:非法访问内存

// 1. 修改常量区的内容
//      尝试修改字符串常量或其他只读数据段的内容会导致段错误
//      例如:char *p = "hello"; *p = 'a'; // 未定义行为,可能导致段错误

// 2. 野指针的间接访问(错误不能预知)
//      野指针是指未初始化或已被释放的指针
//      访问野指针指向的内存会导致不可预测的行为,通常会导致段错误
//      例如:char *p; printf("%c", *p); // p未初始化,访问*p可能导致段错误

// 3. 数组越界(错误不能预知)
//      访问数组中不存在的元素会导致数组越界
//      数组越界可能会导致程序崩溃或覆盖其他变量的内存
//      例如:int arr[5]; arr[5] = 10; // 越界访问,arr[5]不存在

// 4. 空指针的间接访问
//      访问空指针(NULL)指向的内存会导致段错误
//      例如:char *p = NULL; printf("%c", *p); // 访问*p会导致段错误

【10】思维导图

测试题:

  • 输入带空格的字符串,删除字符串中的空格
#include <stdio.h>
#include <string.h>

int main(int argc, const char *argv[])
{
    // 定义一个字符数组 str,大小为 100,用于存储输入的字符串
    char str[100]; 
    // 定义一个字符指针 p,后续用于指向字符串
    char *p; 

    // 输入带空格的字符串
    // scanf("%[^\n]", str); 是一种特殊的 scanf 用法
    // %[^\n] 表示读取除换行符之外的所有字符,直到遇到换行符为止
    // 这样就可以读取包含空格的字符串,并将其存储到 str 数组中
    scanf("%[^\n]", str); 

    // 指针 p 指向字符串的起始位置
    // 让指针 p 指向字符数组 str 的首地址,方便后续通过指针操作字符串
    p = str; 

    // 定义两个整型变量 i 和 j,用于遍历字符串和构建新字符串
    int i, j; 
    // 遍历字符串
    // 使用 for 循环遍历字符数组 str,i 用于遍历原字符串,j 用于构建新字符串
    // 当 str[i] 不等于字符串结束符 '\0' 时,继续循环
    for (i = 0, j = 0; str[i] != '\0'; i++) {
        // 判断当前字符是否为空格
        if (str[i] != ' ') {
            // 如果当前字符不是空格,则将其复制到新字符串的位置
            // 将 str[i] 的字符赋值给 str[j],即把非空格字符依次放到新位置
            str[j] = str[i]; 
            // j 自增,指向下一个新字符串的存储位置
            j++; 
        }
    }
    // 新字符串结束标志
    // 当遍历完原字符串后,在新字符串的末尾添加字符串结束符 '\0'
    // 表示新字符串的结束
    str[j] = '\0';  

    // 输出去除空格后的字符串
    printf("%s", str); 
    // 程序正常结束,返回 0
    return 0; 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Oracle_666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值