2143 字
11 分钟
C语言简明笔记
#include<stdio.h>
int main(){
printf("Hello world\n");
}字符串
- 使str指向字符串的第一个字符。
#include<stdio.h>
int main(){
char *str;
str = "Hello world\n";
printf("%s",str);
}访问字符串中的字符
#include<stdio.h>
int count_spaces(const char s[]){
int c = 0;
int i;
for(i = 0;s[i] != '\0';i ++){
if(s[i] == ' ') c ++;
}
return c;
}
int main(){
char *str;
str = "Hello the world of C\n";
printf("%s",str);
printf("%d\n",count_spaces(str));
}使用 C 语言的字符串库
在 C 语言中把字符串当作数组来处理,因此对字符串的限制方式和对数组的一样,特别是它们都不能用 C 语言的运算符进行复制和比较操作。
C 语言的函数库为完成对字符串的操作提供了丰富的函数集。这些函数的原型驻留在<string.h>头
strncpy()
#include<stdio.h>
#include<string.h>
int main(){
char *str;
str = "Hello the world of C\n";
printf("%s",str);
char str1[10] = "";
char str2[30] = "";
strncpy(str1, str, sizeof(str1) - 1);
strncpy(str2, str, sizeof(str2) - 1);
printf("%s\n",str1);
printf("%s",str2);
}strncat()
#include<stdio.h>
#include<string.h>
int main(){
char *str;
str = "Hello the world of C\n";
printf("%s",str);
char str1[40] = "";
// strncat(目标数组, 源字符串, 目标数组剩余空间)
strncat(str1, str, sizeof(str1) - strlen(str1) - 1);
strncat(str1, str, sizeof(str1) - strlen(str1) - 1);
// 确保字符串以 \0 结尾
str1[sizeof(str1) - 1] = '\0';
printf("%s", str1);
}自实现简单字符串常用函数
substr()
完全版
/**
* @brief 实现C++ string::substr的功能
* @param str 原字符串(不能为空)
* @param pos 起始位置(从0开始计数)
* @param len 要截取的长度(如果为0或超过剩余长度,则截取到字符串末尾)
* @return 截取后的子字符串(需要调用者手动free释放内存),失败返回NULL
*/
char* substr(const char* str, size_t pos, size_t len) {
// 1. 基础参数校验
if (str == NULL) {
printf("错误:原字符串不能为空\n");
return NULL;
}
// 2. 获取原字符串长度
size_t str_len = strlen(str);
// 3. 检查起始位置是否越界
if (pos >= str_len) {
printf("错误:起始位置%zu超出字符串长度%zu\n", pos, str_len);
return NULL;
}
// 4. 计算实际要截取的长度
size_t actual_len;
if (len == 0 || (pos + len) > str_len) {
// 如果len为0或截取范围超出字符串末尾,则截取到末尾
actual_len = str_len - pos;
} else {
actual_len = len;
}
// 5. 分配内存(+1 用于存储字符串终止符'\0')
char* result = (char*)malloc(actual_len + 1);
if (result == NULL) {
printf("错误:内存分配失败\n");
return NULL;
}
// 6. 拷贝子字符串(从pos位置开始,拷贝actual_len个字符)
strncpy(result, str + pos, actual_len);
// 手动添加终止符(strncpy在拷贝满长度时不会自动加)
result[actual_len] = '\0';
return result;
}测试样例
void test(){
const char* original = "Hello, World!";
// 测试1:截取从索引7开始,长度5的子串("World")
char* sub1 = substr(original, 7, 5);
printf("测试1: %s\n", sub1);
free(sub1); // 必须释放内存
// 测试2:截取从索引0开始,长度5的子串("Hello")
char* sub2 = substr(original, 0, 5);
printf("测试2: %s\n", sub2);
free(sub2);
// 测试3:截取长度超出剩余字符(自动截取到末尾)
char* sub3 = substr(original, 5, 20);
printf("测试3: %s\n", sub3);
free(sub3);
// 测试4:起始位置越界(返回NULL)
char* sub4 = substr(original, 20, 5);
if (sub4 == NULL) {
printf("测试4: 截取失败(起始位置越界)\n");
}
}简化板子
char* substr(const char* str, size_t pos, size_t len) {
// 基础参数校验
if (str == NULL) {
exit(0);
return NULL;
}
//获取原字符串长度
size_t str_len = strlen(str);
//检查起始位置是否越界
if (pos >= str_len) {
exit(0);
return NULL;
}
// 计算实际要截取的长度
size_t actual_len;
if (len == 0 || (pos + len) > str_len) {
// 如果len为0或截取范围超出字符串末尾,则截取到末尾
actual_len = str_len - pos;
} else {
actual_len = len;
}
// 5. 分配内存(+1 用于存储字符串终止符'\0')
char* result = (char*)malloc(actual_len + 1);
if (result == NULL) {
exit(0);
return NULL;
}
// 拷贝子字符串(从pos位置开始,拷贝actual_len个字符)
strncpy(result, str + pos, actual_len);
// 手动添加终止符(strncpy在拷贝满长度时不会自动加)
result[actual_len] = '\0';
return result;
}结构、联合和枚举
结构是可能具有不同类型的值(成员)的集合。
联合和结构很类似,不同之处在于联合的成员共享同一存储空间。这样的结果是,联合可以每次存储一个成员,但是无法同时存储全部成员。
枚举是一种整数类型,它的值由程序员来命名。
结构
- 每个结构代表一种新的作用域。任何声明在此作用域内的名字都不会和程序中的其他名字冲突。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
const int NAME_LEN = 30;
struct empolyee
{
int age;
char name[NAME_LEN + 1];
int salary;
};
void print(struct empolyee x){
printf("%d\n",x.age);
puts(x.name);
printf("%d\n",x.salary);
}
int main(){
struct empolyee a = {.name = "Lemon Tree", .salary = 100000, .age = 20};
print(a);
return 0;
}- 除了声明结构标记,还可以用 typedef 来定义真实的类型名。
- 传指针避免值拷贝
{.name = "Lemon Tree", .salary = 100000, .age = 20}被称为指示器
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
// 宏定义规范:常量改用#define且全大写
#define NAME_LEN 30
// 增加typedef简化结构体使用
typedef struct Employee {
int age;
char name[NAME_LEN + 1]; // 字符数组用于存储姓名
int salary;
} Employee;
// 函数优化:
// - 传指针避免值拷贝,const保证参数不被修改
// - 增加空指针断言,提升鲁棒性
void printEmployee(const Employee* emp) {
// 断言防止空指针传入导致程序崩溃
assert(emp != NULL);
printf("%d\n", emp->age);
puts(emp->name);
printf("%d\n", emp->salary);
}
int main() {
Employee a = {.age = 20, .name = "Lemon Tree", .salary = 100000};
// 传地址调用函数,减少内存拷贝
printEmployee(&a);
return 0;
}利用结构整体赋值
- 除了赋值运算,C 语言没有提供其他用于整个结构的操作。
int main() {
Employee a = {.age = 20, .name = "Lemon Tree", .salary = 100000};
// 传地址调用函数,减少内存拷贝
printEmployee(&a);
Employee b = a;
b.age ++;
printEmployee(&b);
return 0;
}联合
#include <stdio.h>
#define INT_KIND 0
#define DOUBLE_KIND 1
typedef struct {
int kind; /* tag field */
union {
int i;
double d;
} u;
} Number;
// 打印 Number 类型的值
void printNumber(const Number *num) {
switch (num->kind) {
case INT_KIND:
printf("Integer: %d\n", num->u.i);
break;
case DOUBLE_KIND:
printf("Double: %.2f\n", num->u.d);
break;
default:
printf("Unknown kind\n");
}
}- 测试样例
void test(){
Number num1, num2;
// 初始化一个整数类型
num1.kind = INT_KIND;
num1.u.i = 42;
// 初始化一个浮点数类型
num2.kind = DOUBLE_KIND;
num2.u.d = 3.14159;
// 打印结果
printNumber(&num1);
printNumber(&num2);
}匿名联合
#include <stdio.h>
#include <assert.h>
// 类型标记宏定义(规范且语义化)
#define NUM_TYPE_INT 1
#define NUM_TYPE_FLOAT 2
// 仅保留匿名联合版本的结构体,typedef简化声明
typedef struct {
int type; // 数据类型标记:1=int,2=float
union { // 匿名联合:成员直接提升到父结构体作用域
int ival; // 整数值(可直接通过结构体变量访问)
float fval; // 浮点数值(可直接通过结构体变量访问)
};
} NumberAnonymous;
// 封装打印逻辑,增加空指针校验和错误处理
void printNumber(const NumberAnonymous* num) {
assert(num != NULL); // 防止空指针传入
switch (num->type) {
case NUM_TYPE_INT:
printf("整数值:%d\n", num->ival); // 直接访问,无需 .u
break;
case NUM_TYPE_FLOAT:
printf("浮点值:%.2f\n", num->fval); // 直接访问,无需 .u
break;
default:
printf("错误:未知的数据类型(type=%d)\n", num->type);
}
}- 测试
void test(){
// 初始化整数类型
NumberAnonymous num1 = {
.type = NUM_TYPE_INT,
.ival = 100 // 直接赋值,无需 .u.ival
};
// 初始化浮点类型
NumberAnonymous num2 = {
.type = NUM_TYPE_FLOAT,
.fval = 3.14159f // 直接赋值,无需 .u.fval
};
// 测试打印
printNumber(&num1);
printNumber(&num2);
}枚举
#include <stdio.h>
#include <assert.h>
typedef struct {
// 用枚举声明标记字段,替代之前的宏
enum { INT_KIND, DOUBLE_KIND } kind;
// 匿名联合,成员直接提升到结构体作用域
union {
int i;
double d;
};
} Number;
// 打印函数:根据枚举标记安全访问联合成员
void printNumber(const Number *num) {
assert(num != NULL);
switch (num->kind) {
case INT_KIND:
printf("整数: %d\n", num->i);
break;
case DOUBLE_KIND:
printf("双精度浮点数: %.2f\n", num->d);
break;
default:
printf("错误: 未知的 Number 类型\n");
}
}- test
void test(){
// 初始化一个整数类型的 Number
Number num1 = {
.kind = INT_KIND,
.i = 42
};
// 初始化一个双精度浮点数类型的 Number
Number num2 = {
.kind = DOUBLE_KIND,
.d = 3.14159
};
printNumber(&num1);
printNumber(&num2);
}本文阅读量: 次