NDK | C 语言复习笔记

2021-03-11  本文已影响0人  彭旭锐

点赞关注,不再迷路,你的支持对我意义重大!

🔥 Hi,我是丑丑。本文 「Android 路线」| 导读 —— 从零到无穷大 已收录,这里有 Android 进阶成长路线笔记 & 博客,欢迎跟着彭丑丑一起成长。(联系方式在 GitHub)

历史上的今天

2019 年 3 月 19 日,计算\pi 创造新纪录。
谷歌宣布来自日本的女性程序员岩尾遥在公司云计算的帮助下打破了\pi的位数世界纪录,将\pi精确到小数点后 31.4 万亿位。据说,计算程序在 25 台虚拟机上运行了 121 天,涉及 170 TB 的数据。—— 《了不起的程序员》


数据类型

C 语言中数据类型分有符号和无符号,默认是有符号的。

类型 同义词 存储空间
signed char 1 字节
int signed int、signed 2 或 4 字节
short signed short、short int、signed short int 2 字节
long signed long、long int、signed long int 4 字节
long long (C99) signed long long、long long int、signed long long int 8 字节
类型 同义词 存储空间
unsigned char 1 字节
unsigned int unsigned 2 或 4 字节
unsigned short unsigned short int 2 字节
unsigned long unsigned long int 4 字节
unsigned long long (C99) unsigned long long int 8 字节

C 语言定义的 int 长度是不比 short 短,不比 long 长。具体长度取决于编译时指定的目标平台。

是「名称 - 替换文本」的映射,预处理时会将源码中出现宏名称的地方展开为指定的替换文本;

宏定义:
#define ARRAY_SIZE 100

使用宏:
double data[ARRAY_SIZE]

带参数的宏 注意宏名称和左括号之间不能有空格,否则会变成无参数的宏。

#define DISTANCE(x, y) ((x)>=(y) ? (x)-(y) : (y)-(x))
int d = DISTANCE(1,2

变量

变量类型 作用域 生命周期 内存区域
局部变量 函数内部 函数 栈区
全局变量 整个项目 进程 数据区
静态局部变量 函数内部 进程 数据区
静态全局变量 源文件内部 进程 数据区

函数

函数类型 作用域 存储位置
全局函数 整个项目 代码区
静态函数 源文件内部 代码区
占位符 含义
%d、%i 有符号整数
%u 无符号整数,最高位当作值的一部分
%o 有符号整数(八进制)
%x 有符号整数(八进制)
%X 有符号整数(十六进制),字母按大写显示
%c 字符
%s 字符串
inline void fun(); // 无效
void fun() {
}
--------------------------
void fun();
inline void fun() { // 有效
}

注意:inline 需要应用在 函数定义 才有效,应用在 函数定义 是无效的。

关键词

static 关键字的作用
const 关键字的作用
const int a = 1;
a = 2; (X)// Cannot assign to variable 'a' with const-qualified type 'const int'

注意:const 和 类型 int 可以互换位置,以下两条语句是等价的。
const int a = 1;
int const a = 1;
const int * p; // 指针指向的变量不可变
int const * p; // 指针指向的变量不可变
int * const p; // 指针指向不可变

const int * const p; // 都不可变
int const * const p; // 都不可变

int a = 1;
const int * p = &a;
*p = 2; (X)// Read-only variable is not assignable
const * int func(); // const 离变量名远,上面提到的 const int * p 的情况。

const 和 define 的对比

const 和 define 有类似之处,它们都定义了一个不允许修改的值,如果只从功能上看,它们都是 “常量”。但是从实现原理上看,它们并不是常量。

  • define 是预编译指令,define 定义的宏在预处理阶段就展开为指定的文本,define 定义的宏在编译后就不存在了。
  • const 是变量修饰符,本质上是一个 只读变量,一般所谓 “const 常量”,是从功能上对 const 的描述。
extern 的作用

main.c

// 如果不使用 extern 修饰,会提示找不到变量。
extern int g ;
g = 1;

extern int g = 1; (X)// 这种写法不允许:'extern' variable cannot have an initializer

other.c

int g;
#ifdef __cplusplus
extern "C" {
#endif

// all of your legacy C code here

#ifdef __cplusplus
}
#endif
volatile 的作用

用于修饰变量,提醒编译器该变量随时可能发生改变,程序每次需要读取变量值的时候,都需要直接从变量地址中读取,而不应该使用寄存器中的缓存。

volatile int a;
include 的作用
#include <系统资源>
#include "项目资源"

数组

字符串

char *p = "xurui";
p[2] = '1'; 不能修改全局区域
printf("%s", p);
char *str = "xurui";
while (*str) {
    ...
    str++
}
函数原型:参数是指向字符串的指针,返回值是长整形
int atoi(const char *str)

调用函数:
#include <stdlib.h>
int result = atoi("1");

类似函数:atol、atof、strtod

怎么判断转换成功?

函数原型:参数是两个字符串指针
int strcmp(const char *str1, const char *str2)
int strncmp(const char *str1, const char *str2, size_t n)

该函数返回值如下:
- 如果返回值小于 0,则表示 str1 小于 str2。
- 如果返回值大于 0,则表示 str1 大于 str2。
- 如果返回值等于 0,则表示 str1 等于 str2。

调用函数:
#include <string.h>
int result = strcmp("1", "2")
函数原型:
char *strstr(const char *haystack, const char *needle)

调用函数:
char *text = "peng xurui";
char *subtext = "x";

char *pop = strstr(text, subtext);
printf("%s\n", pop); // 输出 xurui

int index = pop - text;
printf("索引:%d", index); // 输出 5
函数原型:把 src 字符串追加到 dest 字符串的结尾
char *strcat(char *dest, const char *src)
函数原型:把 c 字符转换为小写字符
int tolower(int c);

调用函数:
char c = 'A';
tolower(c);
函数原型:把 c 字符转换为大写字符
int toupper(int c);

调用函数:
char c = 'a';
tolower(c);

指针

int Func(int x); 声明一个函数
int (*p) (int x); 定义一个函数指针
p = Func; 将 Func 函数的首地址赋给指针变量 p
(*p)(9); 调用函数
(p)(9); 可以省略 *,这里是函数指针特例

堆内存管理

函数原型:参数是分配的字节数
void *malloc(sizt_t size);

调用函数:
#include <stdlib.h>

int *p = (int *) malloc(sizeof int);
if (!p) {
    printf("申请失败");
}
函数原型:参数是元素个数 + 元素占用字节数
void* calloc(size_t num_elements,size_t element_size);

调用函数:
#include <stdlib.h>

int *p = (int *) calloc(5, sizeof(int));
if (!p) {
    printf("申请失败");
}
函数原型:参数是原地址的指针 + 新的内存大小

void realloc(void *ptr, size_t new_size);

调用函数:
#include <stdlib.h>

int *p = (int *) malloc(20 * sizeof(int));
int *p1 = (int *) realloc(p, 21 * sizeof(int));
int *p2 = (int *) realloc(p, 2000 * sizeof(int));

printf("%x\n", p); // 输出 ce4054c0
printf("%x\n", p1); // 输出 ce4054c0
printf("%x\n", p2); // 输出 ce809800(新开辟了一块内存空间)

如果是将分配的内存扩大,则有以下情况:
1)如果当前内存段后面有足够内存空间,则直接扩展这段内存空间,realloc() 返回原指针;
2)否则,在堆中新开辟一块内存,并释放原来的内存块,返回新内存块地址;
2)否则,分配失败返回 NULL。

函数原型:参数是内存块的指针
free(void* pointer);

调用函数:
#include <stdlib.h>

if (p) {
    free(*p);
    p = NULL; // 防止存现悬空指针
}

1)使用 malloc,calloc,realloc 分配的内存,必须通过 free 释放;
2)不能重复释放同一个内存块,因为释放后操作系统可能会把该空间分配给其他用途。

结构体

struct Student {
    char *name;
    int age;
    char sex;
}; // 必须加分号
// 方式 1:
struct Student mark;
mark.name = "mark";
mark.age = 10;
mark.sex = '1';

// 方式 2:
struct Student mark = {"mark", 10, '1'};

注意:结构体中的成员变量初始化为系统值,而不像 Java 会给成员变量初始化为零值。

struct Student {
    char name[10];
    int age;
    char sex;
} mark = {"mark", 10, '1'},
tom, amy;
声明结构体:
struct Student {
    char name[10];
    int age;
    char sex;

    struct Sport {
        char *name;
    } sport;
};

使用结构体:
struct Student mark = {"mark", 10, '1'};
struct Student tom = {"tom", 1, '1'};

mark.sport.name = "basketball";
tom.sport.name = "football";

printf("%s\n", mark.sport.name); // basketball
printf("%s\n", tom.sport.name); // football

注意: 嵌套的 sport 结构体是独立的两个结构体,是独立的两块内存。

结构体指针是指向结构体的指针。

struct Student *p = &mark;
printf("%s\n", p->name); // mark

其中,->是调用指针的一级成员变量。

静态开辟:栈
struct Student mark;
mark.name = "mark";

动态开辟:堆
struct Student *markP = malloc(sizeof(struct Student));
markP->name = "mark";

printf("%s\n", markP->name); // mark

结构体数组是元素为结构体的数组。

静态开辟:栈
struct Student student[3] = {
            {"mark", 10, '1'},
            {"tom",  1,  '1'},
            {},
};
struct Student *p = &student;
    
printf("%s\n", student[0].name); // mark
printf("%s\n", (p + 1)->name); // tom

动态开辟:堆
struct Student *p = malloc(sizeof(struct Student) * 3);
p->name = "mark";
(p + 1)->name = "tom";

printf("%s\n", p->name); // mark
printf("%s\n", (p + 1)->name); // tom

free(p);
定义别名:
typedef struct Student Student_;
typedef struct Student * pStudent;

使用别名:
Student *p = malloc(sizeof(Student) * 3);
pStudent p = malloc(sizeof(Student) * 3);

这里需要注意 Clion 和 VS 两个编辑器的差异化:

Clion 要求使用 struct Student
struct Student *p = malloc(sizeof(struct Student) * 3);
free(p);

VS 可以省略 struct 关键字
Student *p = malloc(sizeof(Student) * 3);
free(p);

为了避免源码在不同编译器不统一,可以定义一个相同的别名:

定义一个相同的别名
typedef struct Student Student;

使用别名:
Student *p = malloc(sizeof(Student) * 3);

提示: 你可以在源码中看到很多结构体都定义了别名,就是为了保持代码统一。C 语言枚举也有类似的问题。

struct { // 匿名结构体
    char *name;
}; // 不能引用,没有任何意义

struct { // 匿名结构体
    char *name;
} a1, a2, a3;

文件操作

函数原型:使用指定 mode 打开文件,返回 FILE 指针
FILE *fopen(const char *filename, const char *mode)

模式:
1)r:读取
2)w:写入
3)a:在文件后追加
4)r+
5)w+
6)a+

使用函数:
FILE *file = fopen("文件名", "r");
if (!file) {
    // 文件打开失败
}

char buffer[10];
while (fgets(buffer, 10, file) {
    printf("%s", buffer);
}
函数原型:
int fputs(const char *str, FILE *stream)

使用函数:
FILE *file = fopen("文件名", "");
if (!file) {
    // 文件打开失败
}

fputs("xurui", file);
fclose(file);

内存区域

—— 图片引用自网络


参考资料

创作不易,你的「三连」是丑丑最大的动力,我们下次见!


上一篇 下一篇

猜你喜欢

热点阅读