C 语言 数组、字符串、函数

发布于 2024-07-19 14:59:50 字数 14185 浏览 11 评论 0

数组初始化的几种方式

#include <stdio.h>

int main(int argc, char const *argv[])
{
    int arr1[5] = {1,2,3,4,5}; //数组的定义需要{},只有在定义的时候才能初始化
    int arr2[5];
    //arr2 = {1,2,3,4,5}; //err 这个是错误的

    //数组全部元素初始化为 某个值
    int arr3[5] = {0};

    //如果定义的同时 初始化,第一个[]内可以不写内容
    int arr4[] = {1,2,3,4,5}; //编译器会根据用户初始化的元素来确定数组的大小
    //int arr4[]; //err //必须初始化的时候才能省略
    int arr5[][4] = {0,1,2,3,4,5,6,7,8,9,10,11}; //第一个可以省略
    return 0;
}

数组的名字代表着数组的首地址,以及使用 sizeof 来求数组长度的方法;

#include <stdio.h>

int main(int argc, char const *argv[])
{
    printf("------------一维数组------------\n");
    
    int arr[10];
    //1. 数组名是数组首元素地址
    printf("arr = %p, &arr[0] = %p\n", arr, &arr[0]); //两个是一样的

    //2. 测试数组总的字节大小 : sizeof(数组名) 10 * 4 = 40
    printf("sizeof(arr) = %u\n",sizeof(arr));

    //3. 得到数组长度 数组总大小/每个元素大小 (常用)
    int len = sizeof(arr)/sizeof(arr[0]);
    printf("len(arr) = %d\n",len);


    printf("------------二维数组------------\n");

    int arr2[5][10];
    // 1. 数组名是常量,不能修改
    // a = 10; //err
    //2. sizeof(数组名),测数组的总大小 5*int[10] = 5*4*10
    printf("sizeof(arr2) = %u\n",sizeof(arr2));
    
    //3. 求行数
    int n = sizeof(arr2)/sizeof(arr2[0]);
    printf("行数 = %d\n",n);

    //4. 求列数
    int m = sizeof(arr2[0])/sizeof(arr2[0][0]);
    printf("列数 = %d\n",m);

    //5. 总个数
    printf("总个数 = %d\n",sizeof(arr2)/sizeof(arr2[0][0]));
    return 0;
}

输出:

------------一维数组------------
arr = 0x7ffc86eac190, &arr[0] = 0x7ffc86eac190
sizeof(arr) = 40
len(arr) = 10
------------二维数组------------
sizeof(arr2) = 200
行数 = 5
列数 = 10
总个数 = 50

可以看出 arrarr[0] 的地址是一样的。

字符数组与字符串

  • C 语言中没有字符串这种数据类型,可以通过 char 的数组来替代;
  • 字符串一定是一个 char 的数组,但 char 的数组未必是字符串;
  • 数字 0 (和字符 ‘\0’ 等价) 结尾的 char 数组就是一个字符串,但如果 char 数组没有以数字 0 结尾,那么就不是一个字符串,只是普通字符数组,所以字符串是一种特殊的 char 的数组。
#include <stdio.h>

int main(int argc, char const *argv[])
{
    // 1. c 语言没有字符串类型,用字符数组模拟
    char str[10];

    // 2. 字符串一定是字符数组,字符数组不一定是字符串
    // 3. 如果字符数组以'\0'(0) 结尾,就是字符串

    char str2[] = {'a', 'b', 'c'};//字符数组 --> 注意这里[]内没有指定数字
    char str3[10] = {'a', 'b', 'c', '\0'}; //字符串
	char str4[10] = {'a', 'b', 'c', 0};    //字符串
    return 0;
}

字符串输出乱码问题以及一系列字符串要注意的问题

#include <stdio.h>

int main(int argc, char const *argv[])
{
    // 1. 字符数组打印乱码问题
    char a1[] = {'a', 'b', 'c'}; //字符数组
    printf("a1 = %s\n",a1); //这个是乱码,因为字符数组后面没有'\0'

    // 2. 正确的字符串
    char a2[] = {'a', 'b', 'c', '\0'};
    //char a2[] = {'a', 'b', 'c', 0}; // 0 也可以

    // 3. 遇到'\0'提前截断
    char a3[] = {'a', 'b', 'c', '\0', 'h', 'e', '\0'};
	printf("a3 = %s\n", a3);//"abc"

    // 4. 前 3 个字符赋值为 a, b, c, 后面自动赋值为 0
    char a4[10] = {'a', 'b', 'c'};
	printf("a4 = %s\n", a4);

    // 5. 常用的初始化方法  --> 使用字符串初始化,在字符串结尾自动加结束符数字 0
    //    这个结束符,用户看不到(隐藏),但是是存在的
    char a5[10] = "abc";
    printf("a5 = %s\n", a5);

    // 6. 使用 sizeof() 测试隐藏的那个'\0'
    char a6[] = "abc";
    printf("sizeof(a6) = %d\n", sizeof(a6));

    // 7. \0 后面最好别跟数字,不然有可能变成转义字符 例如\012 就是'\n'
    char a7[] = "\012abc";
    printf("a7 = %s\n", a7);

    // 8. 最多写 9 个字符,留一个位置放结束符
    char a8[10] = "123456789"; 

    // 9. sizeof() 测数据类型大小,不会因为结束符提前结束  sizeof("123\045") = 5
    char a9[] = "abc\0de";
    printf("%u\n", sizeof(a9));  // 7 --> a b c \0 d e \0  不要忘记最后还有一个\0

    // 10. scanf("%s", a); //a 没有&,原因数组名是首元素地址,本来就是地址
    return 0;
}

输出结果:

随机数生成

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(int argc, char const *argv[])
{
    // 1. 先设置种子,种子设置一次即可
	// 2. 如果 srand() 参数一样,随机数就一样
	//srand(100);

    // 由于希望产生的随机数不同,所以要使用系统时间作为随机数  
    srand( (unsigned int)time(NULL) ); // 返回的是 time_t 类型 相当于 long,单位为毫秒,注意强制转换一下

    for(int i = 0; i < 3; i++){
        printf("the rand number is = %d\n", rand());
    }
    return 0;
}

字符串相关函数

输入函数

gets(str)scanf("%s", str) 的区别:

  • gets(str) 允许输入的字符串含有空格;
  • scanf(“%s”,str) 不允许含有空格;

注意:由于 scanf()gets() 无法知道字符串 s 大小,必须遇到换行符或读到文件结尾为止才接收输入,因此容易导致字符数组越界(缓冲区溢出) 的情况。

fgets 函数 相关:

char *fgets(char *s, int size, FILE *stream);
  • 功能:从 stream 指定的文件内读入字符,保存到 s 所指定的内存空间,直到出现换行字符、读到文件结尾或是已读了 size - 1 个字符为止,最后会自动加上字符 '\0' 作为字符串结束;

  • 参数 :
    s:字符串;
    size:指定最大读取字符串的长度(size - 1)(大于这个长度就舍弃);
    stream:文件指针,如果读键盘输入的字符串,固定写为 stdin;

  • 返回值:
    成功:成功读取的字符串;
    读到文件尾或出错: NULL;

  • 注意, fgets() 在读取一个用户通过键盘输入的字符串的时候,同时把用户输入的回车('\n') 也做为字符串的一部分。通过 scanf 和 gets 输入一个字符串的时候,不包含结尾的“\n”,但通过 fgets 结尾多了“\n”。

  • fgets() 函数是安全的,不存在缓冲区溢出的问题。

简单测试:

#include <stdio.h>

int main(int argc, char const *argv[])
{
    // 1. scanf() 输入字符串 不做越界检查,此函数不安全

    // 2. gets(已抛弃,不安全) 
    char buf[10];
    // gets(buf);  //不安全 已废弃
    // printf("buf = %s\n", buf);

    // 3. fgets(安全,可以指定大小)
    fgets(buf,sizeof(buf),stdin);
    printf("buf = '%s'\n", buf);

    // 4. 测试读入了 最后的换行
    fgets(buf,sizeof(buf),stdin); // 注意虽然这里从键盘读入,但是如果缓冲区还有内容就不会读这里的
    printf("buf2 = '%s'\n", buf);
    return 0;
}

输入输出结果:

输出函数

int puts(const char *s);
功能:标准设备输出 s 字符串,在输出完成后自动输出一个'\n'。
int fputs(const char * str, FILE * stream);  //文件操作
功能: 将 str 所指定的字符串写入到 stream 指定的文件中, 字符串结束符 '\0'  不写入文件。 

简单测试:

#include <stdio.h>

int main(int argc, char const *argv[])
{
    char str[] = "hello world!";
    puts(str); //会自动加上一个 '\n'

    fputs(str,stdout); // 不会自动加上 '\n'
    return 0;
}

输出:

sizeof() 和 strlen() 的区别

注意:

  • strlen() 从首元素开始,到结束符为止的长度,结束符不算(遇到'\0'结束);
  • sizeof() 则不管遇不遇到 '\0' 都会计算整个数据类型大小;
#include <stdio.h>
#include <string.h>

int main(int argc, char const *argv[])
{

    char buf[] = "hello";

    // strlen 从首元素开始,到结束符为止的长度,结束符不算(遇到'\0'结束)
    int len = strlen(buf);
    printf("strlen(buf) = %d\n", len);   //5
    printf("sizeof(buf) = %d\n", sizeof(buf)); // 6 这个还包括 '\0'

    char buf2[] = "\0hello";
    printf("strlen(buf2) = %d\n", strlen(buf2)); // 0
    printf("sizeof(buf2) = %d\n", sizeof(buf2)); // 7  注意不要忘记最后还有一个 '\0'


    char buf3[100] = "zxzxin";
    printf("strlen(buf3) = %d\n", strlen(buf3)); //6
    printf("sizeof(buf3) = %d\n", sizeof(buf3));  //100

    return 0;
}

字符串拷贝 strcpy() 和 strncpy()

注意两者区别:

  • char *strcpy(char *dest, const char *src) :把 src 所指向的字符串复制到 dest 所指向的空间中,'\0'也会拷贝过去。(如果参数 dest 所指的内存空间不够大,可能会造成缓冲溢出的错误情况。)
  • char *strncpy(char *dest, const char *src, size_t n) :把 src 指向字符串的前 n 个字符复制到 dest 所指向的空间中,是否拷贝结束符看指定的长度是否包含'\0'。
#include <stdio.h>
#include <string.h>

int main(int argc, char const *argv[])
{ 

    //strcpy:  把 src 所指向的字符串复制到 dest 所指向的空间中,'\0'也会拷贝过去
    char src[] = "hello world!"; 
    char dest[100] = "aaaaaaaaaaaaaaaaaaaaaaa";
    strcpy(dest,src);
    printf("dest = %s\n", dest);// hello world!  不会输出后面的 aaaa, 因为'\0'也拷贝在后面了

    // strncpy : 把 src 指向字符串的前 n 个字符复制到 dest 所指向的空间中,是否拷贝结束符看指定的长度是否包含'\0'。
    char dest2[100] = "aaaaaaaaaaaaaaaaaaaaaaa";
    strncpy(dest2, src, strlen(src));
    printf("dest2 = %s\n", dest2); //hello world!aaaaaaaaaaa

    //但是如果拷贝的长度大于 strlen(src)
    char dest3[100] = "aaaaaaaaaaaaaaaaaaaaaaa";
    strncpy(dest3, src, strlen(src)+1);
    printf("dest3 = %s\n", dest3); //hello world!
    return 0;
}

输出:

strcat()、strncat()、 strcmp()、strncmp()

  • char *strcat(char *dest, const char *src); : 将 src 字符串连接到 dest 的尾部, ‘\0’ 也会追加过去;
  • char *strncat(char *dest, const char *src, size_t n); : 将 src 字符串前 n 个字符连接到 dest 的尾部, ‘\0’ 也会追加过去;
  • int strcmp(const char *s1, const char *s2); : 比较 s1 和 s2 的大小,比较的是字符 ASCII 码大小;
  • int strncmp(const char *s1, const char *s2, size_t n); :比较 s1 和 s2 前 n 个字符的大小,比较的是字符 ASCII 码大小;

测试:

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

int main(int argc, char const *argv[])
{
    char s1[] = "abc";
    char s2[] = "abcd";
    int flag = strcmp(s1, s2);
    printf("flag = %d\n",flag);// <0

    char s3[] = "abc";
    char s4[] = "Abcd";
    int flag2 = strncmp(s3, s4, 3); //指定比较前 3 个字符
    printf("flag2 = %d\n",flag2);// >0s

    printf("-------------strcat 和 strncat------------\n");
    char src[] = " hello mike";
    char dst[100] = "abc";
    //把 src 的内容追加到 dst 的后面
    //strcat(dst, src); //dst = "abc  hello mike"
    strncat(dst, src, strlen(" hello")); //指定长度追加 dst = "abc  hello"
    printf("dst = %s\n", dst);
    return 0;
}

输出:

flag = -100
flag2 = 32
-------------strcat 和 strncat------------
dst = abc hello

sprintf()、sscanf()

  • int sprintf(char *str, const char *format, ...); : 根据参数 format 字符串来转换并格式化数据,然后将结果输出到 str 指定的空间中,直到出现字符串结束符 '\0' 为止。
  • int sscanf(const char *str, const char *format, ...); : 从 str 指定的字符串读取数据,并根据参数 format 字符串来转换并格式化数据。
#include <stdio.h>

int main(int argc, char const *argv[])
{
    printf("--------------sprintf-------------------\n");
    int n = 10;
    char ch = 'a';
    char buf[10] = "hello";
    char dest[30];
    sprintf(dest,"n = %d, ch = %c, buf = %s\n", n, ch, buf);
    printf("dest: %s", dest);  // 注意这里没有加上 '\n' 但是之前里面有

    printf("--------------sscanf-------------------\n");
    // sscanf 和 spirnf 相反  这里从 dest 中读取
    int n2;
    char ch2;
    char buf2[10];
    sscanf(dest, "n = %d, ch = %c, buf = %s\n", &n2, &ch2, &buf2); //记得加上 &
    printf("n2 = %d\n", n2);
    printf("ch2 = %c\n", ch2);
    printf("buf2 = %s\n", buf2);

    printf("-----------字符串提取注意的地方--------------\n");
    // 从字符串中提取 内容最好按照空格进行分割 ,不然有可能提取不出来
    // 1. 按照空格分割 --> 正确
    char buf3[] = "aaa bbb ccc";
    char a[10],b[10],c[10];
    sscanf(buf3, "%s %s %s", a,b,c); //注意没有&
    printf("a = %s, b = %s, c = %s\n", a, b, c);
    // 2. 按照逗号分割 --> 错误
    char buf4[] = "aaa,bbb,ccc";
    char a2[10],b2[10],c2[10];
    sscanf(buf4, "%s,%s,%s", a2,b2,c2); //注意没有&
    printf("a2 = %s, b2 = %s, c2 = %s\n", a2, b2, c2);

    return 0;
}

结果:

strchr()、strstr()、strtok()

  • char *strchr(const char *s, char c); : 在字符串 s 中查找字母 c 出现的位置;
  • char *strstr(const char *haystack, const char *needle); : 在字符串 haystack 中查找字符串 needle 出现的位置;
  • char *strtok(char *str, const char *delim);
    ①来将字符串分割成一个个片段。当 strtok() 在参数 s 的字符串中发现参数 delim 中包含的分割字符时, 则会将该字符改为\0 字符,当连续出现多个时只替换第一个为\0;
    ②在第一次调用时:strtok() 必需给予参数 s 字符串;
    ③往后的调用则将参数 s 设置成 NULL,每次调用成功则返回指向被分割出片段的指针;
#include <stdio.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    printf("-------------strchr------------\n");
    char str1[] = "aaabbbccc";
    char *p = strchr(str1, 'b');
    printf("p = %s\n", p);

    printf("-------------strstr------------\n");
    char str2[] = "ddddabcd123abcd333abcd";
    char *p2 = strstr(str2, "abcd");
    printf("p2 = %s\n", p2);

    printf("-------------strtok------------\n");
    char str3[100] = "adc*fvcv*ebcy*hghbdfg*casdert";
    char *s = strtok(str3, "*");   //将"*"分割的子串取出
    while (s != NULL){
        printf("%s\n", s);
        s = strtok(NULL, "*");//往后的调用则将参数 s 设置成 NULL,每次调用成功则返回指向被分割出片段的指针
    }
    return 0;
}

输出:

-------------strchr------------
p = bbbccc
-------------strstr------------
p2 = abcd123abcd333abcd
-------------strtok------------
adc
fvcv
ebcy
hghbdfg
casdert

函数

  • 函数内部,包括() 内部的形参变量,只有在调用时分配空间,调用完毕自动释放;
  • returnexit() 函数区别,只要一调用 exit() 函数(不管在什么地方),整个程序就结束,但是只有在 main 函数中调用 return 才会结束程序;
  • 声明函数加不加 extern 关键字都一样, 声明函数可以不指定形参名称,只指定形参形参类型,但是定义不可以。
  • 头文件一般是放函数声明;

看下面两张图解释 .h 文件的作用:

解决办法:

  • 多个文件中(同一项目),不能出现同名函数(static 除外)。这就是为什么 .h 文件只放函数的声明,不放函数的定义;

  • 防止头文件重复包含: 当一个项目比较大时,往往都是分文件,这时候有可能不小心把同一个头文件 include 多次,或者头文件嵌套包含。

防止办法:

#ifndef 方式;

#ifndef __SOMEFILE_H__
#define __SOMEFILE_H__

// 声明语句

#endif

#pragma once 方式。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

春夜浅

暂无简介

0 文章
0 评论
23 人气
更多

推荐作者

内心激荡

文章 0 评论 0

JSmiles

文章 0 评论 0

左秋

文章 0 评论 0

迪街小绵羊

文章 0 评论 0

瞳孔里扚悲伤

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文