2022-01-23

WARNING: This article may be obsolete
This post was published in 2022-01-23. Obviously, expired content is less useful to users if it has already pasted its expiration date.
This article is categorized as "Garbage" . It should NEVER be appeared in your search engine's results.



重新复习C语言

参考资料

🔗 [C 语言教程 | 菜鸟教程] https://www.runoob.com/cprogramming/c-tutorial.html
🔗 [C语言程序设计 浙江大学:翁恺_哔哩哔哩_bilibili] https://www.bilibili.com/video/BV1Ls411w7rx
📖《C程序设计语言》
🔗 [你不见得会计算C字符串长度 - 掘金] https://juejin.cn/post/6844904073150332942

数据类型和范围

最基本的数据类型:

🔗 [C 数据类型 | 菜鸟教程] https://www.runoob.com/cprogramming/c-data-types.html

表格里的数字是“字节/Byte”,1 Byte = 8 bits

解释int范围:

https://www.zhihu.com/question/19580654

浮点类型:

需要区分以下3种字符串初始化语句

char test[1000] = "012abc测试字符串";

char test[] = "012abc测试字符串";

char *test = "012abc测试字符串";

先写总结:

char test[1000];
// 一堆脏数据


char test[1000] = "";
// 会初始化出1000个'\0'


char test[1000] = "012abc测试字符串";
// 会初始化出一个长度为1000的空间,包含开头的"012abc测试字符串"以及一大堆0
// sizeof(test)=1000
// 可以使用strlen(test)找到正确的字符串长度: strlen(test)=21


char test[] = "012abc测试字符串";
// 只会初始化出长度为22的空间(21加上末尾的'\0'所以是22)
// sizeof(test)=22
// strlen(test)=21
// 允许修改字符串内容,比如test[0]='k'或者*(test+0)='k'


char *test = "012abc测试字符串";
// sizeof(test)=8,因为这是指针长度
// strlen(test)=21
// 不允许用任何方法修改里面的内容,比如*(test+1)='k'
// 会导致程序直接错误退出(gcc: Program returned: 139)
// 特别注意,上面这条特性并不是C语言的设计特性,而是编译器的处理,由操作系统提供“只读内存”的实现。
// 部分上古C语言编译器(比如DOS)可能会允许修改字符串的内容。

以下是一些程序:

#include<stdio.h>

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

    char test[1000] = "012abc测试字符串";
    printf("%s\n", test);
    int i = 0;
    for (i = 0; i < 1000; i++) {
        printf("%c", test[i]);
    }
    printf("\n");
    for (i = 0; i < 1000; i++) {
        printf("%d,", test[i]);
    }
    return 0;
}

结果:

这里有必要提前解释一下后面出现的许多 0 ,这是因为我们的代码触发了C语言的“字符串初始化末尾添加\0”这样的操作。注意到下面这两句代码产生的数组空间是完全不一样的:

char arr[1000];
// 一堆脏数据

char arr[1000] = ""; 
// 全部都是\0

char arr[1000] = "abc";
// 除了开头的abc以外,剩下的全部都是\0

验证:

#include<stdio.h>

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

    char arr[10];
    for (int i = 0; i < 10; i++) {
        printf("%d", arr[i]);
    }
    printf("\n");
    char arr2[10] = "";
    for (int i = 0; i < 10; i++) {
        printf("%d", arr2[i]);
    }
    return 0;
}

得到的结果:

回到之前的代码,修改第5行的内容为 char test[] = "012abc测试字符串" ,程序如下:

#include<stdio.h>

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

    char test[] = "012abc测试字符串"; //修改了这一行
    printf("%s\n", test);
    int i = 0;
    for (i = 0; i < 1000; i++) {
        printf("%c", test[i]);
    }
    printf("\n");
    for (i = 0; i < 1000; i++) {
        printf("%d,", test[i]);
    }
    printf("\n");
    return 0;
}

得到这个结果也很合理,因为我们并没有初始化1000这么大的数组空间,所以到了后面for循环打印出来的内容都是超出范围的脏数据。

类似地,第5行改为 char *test = "012abc测试字符串"; 也会出现类似结果.

最后再来讨论一下字符串长度的获取方式,尤其需要注意下面的代码中,获取 int test3[1000] 真实字符串长度的方式(移动指针寻找\0或者使用 strlen() ):

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

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

    char *test = "012abc测试字符串";
    printf("%lu\n", sizeof(test));
    // 8,因为这是指针地址的长度
    char test2[] = "012abc测试字符串";
    printf("%lu\n", sizeof(test2));
    // 22,因为sizeof()是compile time function
    char test3[1000] = "012abc测试字符串";
    printf("%lu\n", sizeof(test3));
    // 1000
    int len = 0;
    char *addr = test3;
    while (*(addr + len) != '\0') len++;
    printf("字符串长度:%d\n", len);
    // 方法1:通过移动指针寻找'\0'判断长度
    // 字符串长度:21,因为没有把'\0'计算在内
    printf("strlen: %lu\n", strlen(test3));
    // 方法2:直接使用strlen()
    // strlen: 21
}

(当然,这不可避免地又扯出了另一个中文字符长度的问题,先放在一边)

继续处理之前的问题,🔗 [你不见得会计算C字符串长度 - 掘金] https://juejin.cn/post/6844904073150332942

这里提到了

再结合🔗 [c - What is the difference between char s[] and char *s? - Stack Overflow] https://stackoverflow.com/questions/1704407/what-is-the-difference-between-char-s-and-char-s

总结:

sizeof和strlen

部分内容已经在上一个目录里面提到了。

对应程序:

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

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

    char greeting[] = {'h', 'e', 'l', 'l', 'o'};
    printf("%ld\n", sizeof(greeting));
    printf("%ld\n", strlen(greeting));

    char greeting2[] = {'h', 'e', 'l', 'l', 'o', '\0'};
    printf("%ld\n", sizeof(greeting2));
    printf("%ld\n", strlen(greeting2));

    return 0;
}

结果:

5
11 // 这个结果是不确定的
6
5

其中,printf  strlen(greeting) 的结果是不固定的,不同环境下得到的结果可能都会不一样,这是因为我们没有给字符串数组末尾手动添加\0,这样会导致strlen()找不到正确的字符串结尾。

size_t类型

好像暂时理解起来有些困难:🔗 [size_t 这个类型的意义是什么? - 知乎] https://www.zhihu.com/question/24773728

const

p31

malloc和calloc

malloc和calloc的区别:calloc() 会 强制把分配得到的空间都统一初始化为0,而malloc() 不会 。但现实编写程序的过程中,我们仍然会发现很多时候malloc()获取的空间也是一堆初始化的0,注意这并不是malloc()导致的,而是操作系统的行为导致的。见:🔗 [c - Why does malloc initialize the values to 0 in gcc? - Stack Overflow] https://stackoverflow.com/questions/8029584/why-does-malloc-initialize-the-values-to-0-in-gcc

此外还有用法上的区别:

int *arr = (int *) malloc(10 * sizeof(int));
// malloc只需要传入一个参数


int *arr = (int *) calloc(10, sizeof(int));
// calloc需要传入2个参数:分配的元素个数,元素的大小

定义:

p141~p142
#include<stdio.h>
#include<string.h>
#include <stdlib.h>

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

    int size = 10;
    int i;
    char *str = (char *) malloc(size);
    str[0] = 'a';
    str[1] = 'b';
    str[2] = 'c';
    for (i = 0; i < size; i++) {
        printf("%c", str[i]);
    }
    printf("\n");
    for (i = 0; i < strlen(str); i++) {
        printf("%c", str[i]);
    }
    printf("\n");
    for (i = 0; i < sizeof(str); i++) {
        printf("%c", str[i]);
    }
    printf("\n");

    free(str);


    return 0;
}

结果:

abc
abc
abc

看起来和本文前面的 char *str="abc"; // 不可修改str的值 有些冲突,当然其实并不冲突:

🔗 [c - When to use malloc for char pointers - Stack Overflow] https://stackoverflow.com/questions/1788655/when-to-use-malloc-for-char-pointers

另一个小总结:

对这个小总结的代码验证:

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

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

    char str[100];
    strcpy(str, "something");
    printf("%s\n", str);
    /*
     * 以下代码都会报错
    char str2[];
    strcpy(str2, "something else");
    printf("%s\n", str2);
     
    char str3[5];
    strcpy(str3, "something");
    printf("%s\n", str3);
     */

    return 0;
}

结果:

something

free

经常和malloc(), calloc()搭配使用。

int *arr = (int *) malloc(sizeof(int) * 10);
free(arr);

但我们有的时候会发现一个问题:

假设我们现在用 int *arr = (int *) malloc(sizeof(int) * 10) 语句申请了一块长度为10的int数组空间,我们只得到了数组的头指针。也就是说,在后续的代码里,如果我们“忘记”了申请数组的长度10,我们理论上就没有什么合法的办法来获取这个数字“10”了。但使用free()来释放空间的时候,我们发现free()并不需要我们提供数组的长度(甚至不接受int size这样的参数),这看起来似乎非常反直觉。它怎么就知道我们需要释放多少空间?

这几设计到复杂的内存分配器的实现机制了,比如:

https://www.zhihu.com/question/302440083

详细内容见:🔗 [free()函数如何知道要释放的空间大小? - 知乎] https://www.zhihu.com/question/302440083

memcpy

https://www.runoob.com/cprogramming/c-function-memcpy.html
#include<stdio.h>
#include<string.h>
#include <stdlib.h>

void case1();

void case2();

void case1() {
    char dest[] = "abcdefg";
    char src[] = "***";
    printf("Before: %s\n", dest);
    memcpy(dest, src, strlen(src));
    printf("After: %s\n", dest);
}

void case2() {
    char dest[] = "abcdefg";
    char src[] = "***";
    printf("Before: %s\n", dest);
    memcpy(dest, src, strlen(src) + 1); // 复制了'\0'过去,所以printf会提前结束
    printf("After: %s\n", dest);
}

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

    printf("Case 1: \n");
    case1();
    printf("\nCase 2: \n");
    case2();

    return 0;
}

结果:

Case 1:
Before: abcdefg
After: ***defg

Case 2:
Before: abcdefg
After: ***

内存空间分配(stack, heap, static等)

比想象中要复杂难学一些(尤其是涉及到对godbolt.org assembly语句的学习),暂时先学这么多吧:

🔗 [C语言:内存分配 - 知乎] https://zhuanlan.zhihu.com/p/52125577


OCR辅助查找文字工具

char 单字节,可以存放字符集中一个字符。 int 整数,一般反映了宿主机上整数的自然大小。 float 单精度浮点数。 double 双精度浮点数。 整数类型 下表列出了关于标准整数类型的存储大小和值范围的细节: 类型 存储大小 值范围 char 1字节 -128 到127 或0到255 unsigned char 1 字节 0 到 255 signed char 1字节 -128 到 127 int 2或4字节 -32,768 到 32,767 或-2,147,483,648 到2,147,483,647 unsigned int 2或4字节 0 到65,535 或0到4,294,967,295 short 2 字节 -32,768 到 32,767 unsigned short 2 字节 0到 65,535 long 4字节 -2,147,483,648 到 2,147,483,647 unsigned long 4 字节 0 到 4,294,967,295注意,各种类型的存储大小与系统位数有关,但目前通用的以64位系统为主。 以下列出了32位系统与64位系统的存储大小的差别 (windows 相同): Windows vc12 Linux gcc-5.3.1 Compiler win32 ×64 1686 ×86 64 Target 1 char 1 1 unsigned char 00 00卜0卜卡牛卜+NNH HW(寸寸寸寸寸0寸 HNN寸寸000 short unsigned short int unsigned int long unsigned long 4 float 0O 00 double long int 8 8 long long 12 16 long double比如说在win32下, int的范围是 -2^31~ 2^31 - 1 也就是:[-2147483648, 2147483647]; 写成16进制:[80 00 00 00, 7F FF FF FF];对于 char greeting [4] =“blog”其实是定义一个长度为4的字符数组,但是字符串”blog”实际是要 包括结束符 \O的,也就是说下面的代码 char greeting [4] = “blog”; 复制代码 本质和下面代码是一样的,如下: char greeting[] = {“b”, “L’, “o’,’g”}; 复制代码 显然是不正确的,那我们修改一下代码,如下: int main(int argc, const char *argv[]) 复制代码 { //注意这里是 5 char greeting [5] 二 “blog”;The difference here is that char *S = “Hello wor ld”; will place “Hello world” in the read-only parts of the memory, and making S a pointer to that makes any writing operation on this memory ilegal. While doing: char s[] 二 “Hello world”; puts the literal string in read-only memory and copies the string to newly allocated memory on the stack. Thus making s [O] =‘〕; legal.以下几种方式表示的都是C字符串的正确表达方式。 /!要以’\0’结尾 复制代码 char greeting [6] = {‘H’,’e’ !1’,’’, “o’,”10’}; /!要以’\0’结尾 char greeting[] = {‘H’, “e’ “L’, “L’, “o’, “10’}; // 默认会在末尾增加”\0” char greeting[] = {“He lLo”}; //上面的简写形式 char greeting [] = “He lLo”; // 默认会在末尾增加”\0” char *greeting = “He lLo”;函数 strlen 返回字符串里的字符数,不包括终止字符’\o’,这里注意 strlen 是一个C的函数,而 sizeof 只是一个操作符。 我们知道,sizeof操作符的参数可以是数组、指针、类型、对象、函数等,函数 strlen 的参数只能是 字符串。 对于 sizeof,其参数不同时,其返回的值也不一样,如下: 1、数组:编译时分配的数组空间大小;2、指针:存储该指针所用的空间大小(32位机器上是4,64位机器 上是8);3、类型:该类型所占的空间大小;4、对象:对象的实际占用空间大小(这个指的是在 C++ 中); 5、函数:函数的返回类型所占的空间大小。函数的返回类型不能是 void 类型;在变量说明中可以用const限定符限定.该限定符用于指定该变量的值不能改变。对于数组, const限定符使数组所有元素的值都不能改变: ccnst double e = 2.71828182845905; const char msg:. *warning:”: coust说明也可用于数组变元,表明函数不能改变数组的值: int strlen (const cnar1l!; 如果试图修改const限定的值、那么所产生的后果取决丁具体实现。西数malloc和calloc用十动态地分配有储空问。语句 void *malloc (size_t n) 142 C程序设计语言 在分配成功时,返回一个指向n字节大小的末初始化的存储空间的指针,秀则返间NULL。而语 句 void *calloc {size_t n, 9ize_1 size) 在分配成功时,返回一个指向足以容纳由n个指定大小的对象组成的数组的存储空间的指针,否 则返回NULL。该存储空间的每一字节均初始化为0。 由 malloc或calloc函数返回的指针能自动地根据所分配对象的类型进行适当的对齐调整,便 它必须强制转换为恰当的类型,正如下例所示: int *ip; ipxlint *〕 c:alloc in, sizeoflint)]; free(p) 函数用于释放p所指向的存储空间,其中p是此前通过调用malloc或calloc函数而得到 的指针。存储空间的释放顺序没有什么限制,但释放一个不是通过调用malloc或calloe函数而得 到的指针所指向的存储空间是一个可怕的错误。malLoc is for allocating memory on the free-store. If you have a string literal that you do not want to modify the following is ok: char *literal = “foo” However, i you want to be able to modify it, use it as a buffer to hold a line of input and SO on, use malloc: char *buf 二 (char) malloc(BUFSIZE);/ define BUFSIZE before / // free(buf);Use malloc() when you don’t know the amount of memory needed during compile time. In case if you have read-only strings then you can use const char str 二 “’something”; Note that the string is most probably be stored in a read-only memory location and you’ll not be able to modify it. On the other hand if you know the string during compiler time then you can do something like: char strl10l; strcpy(str,”Something”);Here the memory is allocated from stack and you will be able to modify the str. Third case is allocating using malloc. Lets say you don’r know the length of the string during compile time. Then you can do char* str = malloc(requiredMem);strcpy(str, “Something”);free(str);描述 C库函数 void *memcpy(void *str1, const void *str2, size_tn) 从存储区 str2 复制n 个字节到存储区 str1。 声明 下面是 memcpyO 函数的声明。 void *memcpy (void *str1, const void *str2, size_t n) 参数 str1--指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。 str2--指向要复制的数据源,类型强制转换为 void* 指针。 1—要被复制的字节数。 返回值 该函数返回一个指向目标存储区 str1 的指针。


 Last Modified in 2023-02-22 

Leave a Comment Anonymous comment is allowed / 允许匿名评论