字符串
strlen
sizeof(“hello”);
strlen
#include<string.h>
size_t strlen(const char *str) {
const char *s;
for (s = str; *s; ++s);
return (s - str);
}
strcpy
#include <stdio.h>
#include <string.h>
int main() {
char source[] = "Hello, World!";
char destination[20];
// 使用 strcpy 复制字符串
strcpy(destination, source);
// 打印复制后的字符串
printf("Source: %s\n", source);
printf("Destination: %s\n", destination);
return 0;
}
char *strcpy_custom(char *dest, const char *src) {
// 检查输入是否为NULL
if (dest == NULL || src == NULL) {
return NULL;
}
// 逐字符复制,包括字符串结尾的 '\0'
while ((*dest++ = *src++) != '\0') {
// 继续复制
}
// 返回目标字符串的起始地址
return dest;
}
strcmp
strcmp(a,b)==0表示两个字符串相同,
不相等的字符串返回的数字是第一个不相等的字符的ASCII的差值
a==b表示两个字符串地址相同
int strcmp_custom(const char *str1, const char *str2) {
while (*str1 != '\0' && *str2 != '\0') {
if (*str1 != *str2) return (*str1 - *str2);
str1++;
str2++;
}
return (*str1 - *str2);
}
`strcmp` 函数是用于比较两个字符串的标准库函数。它返回一个整数值,用于表示两个字符串的大小关系。具体来说,返回值的含义如下:
- 如果两个字符串相等,返回 0。
- 如果第一个字符串大于第二个字符串,返回一个正整数。
- 如果第一个字符串小于第二个字符串,返回一个负整数。
`strcmp` 函数的原理是逐个比较两个字符串的对应字符,直到发现不同的字符或者其中一个字符串结束。比较是按照字符的 ASCII 码值来进行的。
1. 逐个比较对应位置的字符,比较的条件是字符不等于终止符 `\0`。
2. 如果发现不同的字符,返回它们的 ASCII 码差值。
3. 如果其中一个字符串结束(即遇到了 `\0`),而另一个字符串还有字符,返回它们的长度差值。
这个实现只考虑了字符的 ASCII 码值,没有处理本地化(国际化)字符和其他特殊情况。在实际开发中,可以使用标准库提供的 `strcmp` 函数,它会更好地处理这些情况。
输入
fgets
char w[100];
fgets(w,sizeof(w),stdin);//stdin==file
/*解释
`fgets` 函数是 C 语言中用于从输入流(比如键盘输入、文件等)读取一行字符串的函数。它的声明如下:
```
char *fgets(char *str, int n, FILE *stream);
```
- `str`:指向一个字符数组的指针,用于存储读取到的字符串。
- `n`:指定要读取的最大字符数(包括终止符 `\0`),即指定 `str` 的大小。
- `stream`:指向一个 `FILE` 结构的指针,表示输入流,可以是 `stdin`(标准输入,键盘输入)、文件指针等。
`fgets` 从指定的输入流读取字符,直到达到以下条件之一:
1. 读取了 `n - 1` 个字符。
2. 读取到换行符 `\n`。
3. 到达文件尾(EOF)。
然后将读取到的字符存储到 `str` 中,并在末尾添加一个 null 字符 `\0` 以构成 C 风格字符串。
具体到你提供的例子:
```c
char w[100];
fgets(w, sizeof(w), stdin);
```
这段代码从标准输入(键盘输入)读取一行字符串,最多读取 `sizeof(w) - 1` 个字符(因为需要为字符串末尾的 null 字符留一个位置),并将其存储到数组 `w` 中。
如果输入的字符串长度超过 `sizeof(w) - 1`,`fgets` 会截断字符串,并在 `w` 的末尾添加 null 字符。如果输入的字符串没有包含换行符,`fgets` 会一直读取直到达到指定的最大字符数。
注意:`fgets` 会保留输入中的换行符 `\n`,因此如果输入的字符串包含换行符,它也会被存储在 `w` 中。如果你希望去掉换行符,可以使用 `strcspn` 函数:
```c
w[strcspn(w, "\n")] = '\0';
```
这样可以将换行符替换为 null 字符。*/
scanf
char w[100];
scanf("%s",w);//没有&,因为w已经代表了字符数组的地址,不需要&再取地址
/*解释
这段代码使用 `scanf` 从标准输入中读取一个字符串,并将其存储到数组 `w` 中。
- `%s` 是 `scanf` 的转换说明符之一,用于读取字符串。它会从输入中跳过空白字符,然后读取字符直到遇到下一个空白字符(空格、换行符等)为止,或者达到字符串的最大长度。
- `w` 是一个字符数组,用于存储读取到的字符串。需要确保数组足够大以容纳读取的字符串。
这段代码的执行过程如下:
1. `scanf` 遇到 `%s`,会从输入中跳过空白字符,然后读取字符直到遇到下一个空白字符为止,并将读取到的字符存储到数组 `w` 中。
2. 如果输入的字符串长度超过数组 `w` 的大小,可能会导致缓冲区溢出,因此在实际使用中,需要确保数组足够大以容纳可能的输入。
3. 注意,`%s` 不会读取空白字符,因此输入中的空格、制表符等会被当作字符串的结束符。如果需要包含空白字符,可以使用 `%99[^\n]`,其中 `99` 是数组 `w` 的大小减去一个用于存储 null 字符的空间,`[^\n]` 表示读取到换行符为止。
总体来说,`scanf("%s", w)` 用于从标准输入中读取一个字符串,并将其存储到数组 `w` 中。*/
输出
print %s
char originalString[100] = "Hello, World!";
printf("Result: %.*s\n",1,originalString);
printf后.*s可以输出字符串指定前n个字符
动态内存
mallor和free
#include <stdlib.h>
比如说,创建一个字符串数组 char *w[100];
那么动态分配内存 需要
for(int i=0;i<n;i++){
w[i]=(char *)malloc(10*sizeof(char));
// scanf.....;
}
for(int i=0;i<n;i++){
free(w[i]);
}
需要注意的是,一旦free,那么就不能再调用那个字符串,只能确保后面不再需要那部分字符串才能free
/*
malloc 函数
malloc(memory allocation 的缩写)用于在运行时从堆内存中分配一块指定大小的连续内存块。其基本语法如下:
c
void *malloc(size_t size);
size 参数表示要分配的内存块的大小,以字节为单位。
返回值是一个指向新分配内存的指针。如果分配失败,返回 NULL。
使用示例:
c
int *arr = (int *)malloc(5 * sizeof(int));
if (arr == NULL) {
// 处理内存分配失败的情况
} else {
// 内存分配成功,可以使用 arr 指向的内存
}*/
free
/*free 函数
free 用于释放由 malloc、calloc 或 realloc 分配的动态内存。其基本语法如下:
c
void free(void *ptr);
ptr 参数是要释放的内存块的指针,该指针应该是之前调用 malloc 或相关函数返回的指针。
使用示例:
c
int *arr = (int *)malloc(5 * sizeof(int));
// 使用 arr 指向的内存
free(arr); // 释放内存
arr = NULL; // 将指针置为 NULL,防止野指针
注意事项:
使用 free 释放内存后,建议将指针置为 NULL,以防止出现野指针。
不要尝试释放已经被释放的内存块,这可能导致不可预测的行为。
动态内存的管理需要谨慎操作,确保在使用完内存后及时释放,避免内存泄漏。同时,使用 free 后避免继续使用指向已经释放内存的指针,以防止悬空指针的问题。*/
查找
字符串查找字符
位置
#include <string.h>
char w[100];
fgets(w,sizeof(w),stdin);
char *find=strchr(w,'-');//strchr返回一个指向该字符的指针,否则返回NULL。
if(find!=NULL) printf("%d",find-w);
else printf("no find");
/*
`strchr` 函数用于在字符串中查找指定字符的第一个匹配项,并返回指向该字符的指针。如果未找到指定字符,`strchr` 返回 `NULL`。
函数原型如下:
```c
char *strchr(const char *str, int character);这里的int是因为储存的ASCII
```
- `str`: 要在其中搜索的 C 字符串。
- `character`: 要查找的字符。
`strchr` 会从字符串的开头开始搜索,直到找到字符为止。找到的话返回指向该字符的指针,否则返回 `NULL`。
示例用法:
```c
#include <stdio.h>
#include <string.h>
int main() {
const char *str = "Hello, World!";
char target = 'o';
char *result = strchr(str, target);
if (result != NULL) {
printf("Character found at position: %ld\n", result - str);
} else {
printf("Character not found.\n");
}
return 0;
}
在这个例子中,`strchr` 在字符串 "Hello, World!" 中查找字符 'o',找到后返回指向 'o' 的指针,通过指针减去字符串的起始地址,我们得到字符 'o' 在字符串中的位置。*/
char *strchr(const char *str, int character) {
while (*str != '\0') {
if (*str == (char)character) {
return (char *)str; // 返回匹配字符的指针
}
str++;
}
return NULL; // 未找到匹配字符,返回NULL
}
返回值实际上是character后面的字符串(包含第一个character)
数量
char w[100];
fgets(w,sizeof(w),stdin);
int sum=0;
char *zhi=w;
while((zhi=strchr(zhi,'-'))!=NULL){
zhi++;//此时zhi的第一个字符解释‘-’,所以要+1
sum++;
}
printf("%d",sum);
字符串查找子字符串
位置
int main() {
char haystack[] = "Hello,world!world";
char needle[] = "world";
char *find=strstr(haystack,needle);
if(find!=NULL) printf("%d",find-haystack);
else printf("no find");
}
数量
int main() {
char haystack[] = "Hello,world!world";
char needle[] = "world";
char *result =(char *)haystack;
int sum=0;
while((result=strstr(result,needle))!=NULL){
sum++;
result++;
}
printf("%d",sum);
}
可直接调用的函数
绝对值
在C语言中,你可以使用`abs`函数来计算一个数的绝对值。`abs`函数定义在`stdlib.h`头文件中。下面是一个简单的例子:
```c
#include <stdio.h>
#include <stdlib.h>
int main() {
int num = -5;
int absNum = abs(num);
printf("The absolute value of %d is %d\n", num, absNum);
return 0;
}
```
在这个例子中,`abs`函数接受一个整数参数并返回其绝对值。输出将是:
```
The absolute value of -5 is 5
```
请确保在使用`abs`函数之前包含`stdlib.h`头文件。
幂
在C语言中,可以使用`pow`函数来计算幂。`pow`函数定义在`math.h`头文件中。下面是一个例子:
```c
#include <stdio.h>
#include <math.h>
int main() {
double base = 2.0;
int exponent = 9;
double result = pow(base, exponent);
printf("%lf to the power of %d is %lf\n", base, exponent, result);
return 0;
}
```
在这个例子中,`pow`函数接受两个参数:底数和指数。它返回底数的指数次幂。在这里,`2.0`的`9`次方被计算,并将结果存储在`result`中。
请确保在使用`pow`函数之前包含`math.h`头文件。值得注意的是,`pow`函数返回一个`double`类型的浮点数,即使指数是整数。
文件
创建/打开
FILE *file = fopen("zzz.txt", "w");
or
// 创建并初始化文件 a.txt
FILE *fileA = fopen("a.txt", "w");
if (fileA == NULL) {
printf("无法打开文件 a.txt\n");
return 1;
}
检查
// 检查文件是否成功打开
if (file == NULL) {
perror("Error opening file");
return 1;
}
写入文件
// 写入内容到文件
fprintf(file, "Hello, this is the content of zzz.txt!\n");
关闭文件
fclose(file);
删除
#include <stdio.h>
int main() {
// 文件路径
const char *filename = "b.txt";
// 删除文件
if (remove(filename) == 0) {
printf("File %s deleted successfully.\n", filename);
} else {
perror("Error deleting file");
}
return 0;
}
结构体
结构体在C语言中有很多用法,主要用于组织和存储相关的数据。以下是结构体的一些常见用法:
1. **组织相关数据:** 结构体允许你将不同类型的数据组织在一起,形成一个逻辑上的单元。这对于描述一个实体或记录多个属性的对象非常有用。
```c
struct Person {
char name[50];
int age;
float height;
};
```
2. **创建数据类型:** 结构体可以被用来创建自定义的数据类型,使代码更加模块化和可读。这在大型项目中尤为有用。
```c
typedef struct {
int hour;
int minute;
int second;
} Time;
```
3. **传递复杂数据:** 当函数需要返回多个相关的值时,可以使用结构体。这样可以将多个数据打包到一个结构体中,作为函数的返回值。
```c
struct Point {
int x;
int y;
};
struct Point getCoordinates() {
struct Point p;
p.x = 10;
p.y = 20;
return p;
}
```
4. **链表和树的节点:** 在实现链表和树等数据结构时,结构体通常被用来定义节点的结构。
```c
struct ListNode {
int data;
struct ListNode* next;
};
```
5. **文件操作:** 在文件读写中,结构体可以用来定义文件中的记录格式,便于读取和写入。
```c
struct Student {
char name[50];
int rollNumber;
float marks;
};
```
6. **共用体(Union):** 共用体是一种特殊的结构体,允许在同一内存位置存储不同类型的数据。这在某些特定的应用场景下非常有用。
```c
union Data {
int i;
float f;
char str[20];
};
```
这些只是结构体的一些常见用法,实际上,结构体的灵活性使得它可以适应各种数据组织和存储的需求。
结构体和指针
#include <stdio.h>
struct zzz {
int x;
int y;
} ppp[4];
int main() {
struct zzz *point = ppp;
// 输入
for (int i = 0; i < 4; i++) {
scanf("%d %d", &point->x, &point->y);
point++;
}
// 重新指向数组起始位置
point = ppp;
// 输出
for (int i = 0; i < 4; i++) {
printf("%d %d\n", point->x, point->y);
point++;
}
return 0;
}
共用体union
当我们谈论 `union` 时,我们实际上在谈论一种特殊的数据类型,它允许在相同的内存位置存储不同的数据类型。以下是对上述例子的详细解释:
### 1. 存储不同数据类型的值:
```c
#include <stdio.h>
union Data {
int intValue;
float floatValue;
char stringValue[20];
};
int main() {
union Data myData;
// 定义一个联合类型 Data,它有三个成员:intValue、floatValue、stringValue
// 这三个成员共享同一块内存
myData.intValue = 42;
printf("intValue: %d\n", myData.intValue);
myData.floatValue = 3.14;
printf("floatValue: %f\n", myData.floatValue);
// stringValue 和 intValue 共享同一块内存
myData.intValue = 123;
printf("intValue: %d\n", myData.intValue);
printf("stringValue: %s\n", myData.stringValue);
return 0;
}
```
**解释:**
- `union Data` 定义了一个联合类型,可以存储整数、浮点数或字符串。
- `myData` 是这个联合类型的一个实例。
- 不同的成员共享同一块内存。当我们修改一个成员的值,其他成员的值也可能受到影响。
### 2. 用于内存节省:
```c
#include <stdio.h>
union MemorySaving {
int intValue;
char stringValue[sizeof(int)];
};
int main() {
union MemorySaving myMemorySaving;
// 这个联合用于展示如何通过不同类型的成员访问相同的内存
myMemorySaving.intValue = 42;
// 通过 stringValue 访问 intValue 存储的值
printf("intValue: %d\n", *(int *)(myMemorySaving.stringValue));
return 0;
}
```
**解释:**
- 在这个例子中,`union MemorySaving` 中有一个 `int` 类型的成员 `intValue` 和一个 `char` 数组类型的成员 `stringValue`。
- `stringValue` 的大小被设置为 `sizeof(int)`,这意味着它有足够的空间容纳一个整数。
- 通过访问 `stringValue`,我们可以像访问整数一样访问 `intValue` 存储的值。
### 3. 用于位操作:
```c
#include <stdio.h>
union BitFields {
struct {
unsigned int bit1 : 1;
unsigned int bit2 : 1;
unsigned int bit3 : 1;
unsigned int bit4 : 1;
} flags;
unsigned int value;
};
int main() {
union BitFields myBitFields;
myBitFields.value = 0;
// 这个例子展示了如何使用位域进行位操作
myBitFields.flags.bit1 = 1;
myBitFields.flags.bit2 = 0;
myBitFields.flags.bit3 = 1;
myBitFields.flags.bit4 = 0;
printf("value: %u\n", myBitFields.value);
return 0;
}
```
**解释:**
- `union BitFields` 包含一个 `struct` 类型的成员 `flags` 和一个 `unsigned int` 类型的成员 `value`。
- 在 `struct` 中,每个成员都被定义为占用一个比特位。
- 我们可以通过修改 `flags` 的成员来修改 `value`。
总的来说,`union` 提供了一种灵活的方式来使用相同的内存位置存储不同类型的数据,这对于某些特定的应用场景非常有用。然而,由于 `union` 共享内存,一次只能存储一个成员的值,因此在使用时需要小心确保数据的正确性。
枚举enum
#include <stdio.h>
// 定义一个带有名称的枚举
enum Month {
JANUARY = 1,
FEBRUARY,
MARCH,
APRIL,
MAY,
JUNE,
JULY,
AUGUST,
SEPTEMBER,
OCTOBER,
NOVEMBER,
DECEMBER
};
int main() {
// 声明一个带有名称的枚举变量
enum Month currentMonth = JANUARY;
// 输出
printf("Current month: %d\n", currentMonth);
return 0;
}
枚举的主要优势在于提高代码的可读性和可维护性。它可以使你的代码更加清晰地表达意图,特别是在一些常量有关的场景。以下是枚举的一些优势:
1. **可读性强:** 枚举提供了一种更加直观的方式来表示一组相关的常量。通过使用枚举,你可以使用有意义的名称而不是硬编码的数字,使得代码更易读。
2. **易于维护:** 如果你需要修改常量的值或者添加新的常量,使用枚举会更加方便。你只需要修改枚举定义而不用在整个代码中查找和修改每一个具体的常量。
3. **类型安全:** 枚举提供了一种类型安全的方式来表示一组常量。这意味着你不能将不同枚举类型的值混淆使用,从而减少了错误的可能性。
4. **避免硬编码:** 枚举可以避免在代码中硬编码数字,提高了代码的可维护性。通过使用枚举,你可以为常量赋予有意义的名称,使得代码更加清晰。
虽然在某些场景下,直接使用整数或字符串等方式也可以实现相同的功能,但是使用枚举可以使代码更加易读、易维护和类型安全。