文章目录
- 12 操作符
- 🍒算术操作符
- 🍒关系操作符
- 🍒逻辑操作符
- 🍒位操作符
- 🍒赋值操作符
- 🍒强制类型转换符
- 🍒成员访问操作符
- :fire:操作符优先级
- 计算大小符`sizeof`
- 条件操作符 `? :`
- `const char *`
- 按位取反
- `snprintf`字符串输出函数
12 操作符
🍒算术操作符
- 加法 (
+): 加法操作。 - 减法 (
-): 减法操作。 - 乘法 (
*): 乘法操作。 - 除法 (
/): 除法操作。 - 取模 (
%): 返回两个数相除的余数。
#include <stdio.h>int main() {// 算术操作符int a = 10, b = 3;printf("Arithmetic Operators:\n");printf("a + b = %d // Expected: 13\n", a + b); // 加法printf("a - b = %d // Expected: 7\n", a - b); // 减法printf("a * b = %d // Expected: 30\n", a * b); // 乘法printf("a / b = %d // Expected: 3\n", a / b); // 除法, 注意这是整数除法printf("a %% b = %d // Expected: 1\n\n", a % b); // 取模, 注意%%用于打印%return 0;
}
🍒关系操作符
在 C 语言中,关系操作符同样用于比较两个值或表达式,并基于比较的结果返回一个布尔值,即 0 (表示 False)或 1 (表示 True)。C 语言中的关系操作符包括:
==:等于。如果两边的值相等,返回 1;否则返回 0。!=:不等于。如果两边的值不相等,返回 1;否则返回 0。<:小于。如果左边的值小于右边的值,返回 1;否则返回 0。>:大于。如果左边的值大于右边的值,返回 1;否则返回 0。<=:小于或等于。如果左边的值小于或等于右边的值,返回 1;否则返回 0。>=:大于或等于。如果左边的值大于或等于右边的值,返回 1;否则返回 0。
#include <stdio.h>int main() {int a = 10, b = 3;printf("Relational Operators:\n");// 等于printf("a == b: %d\n", a == b); // Expected: 0 (false)// 不等于printf("a != b: %d\n", a != b); // Expected: 1 (true)// 大于printf("a > b: %d\n", a > b); // Expected: 1 (true)// 小于printf("a < b: %d\n", a < b); // Expected: 0 (false)// 大于等于printf("a >= b: %d\n", a >= b); // Expected: 1 (true)// 小于等于printf("a <= b: %d\n", a <= b); // Expected: 0 (false)return 0;
}
🍒逻辑操作符
在 C 语言中,逻辑操作符用于组合或修改条件表达式的布尔值,以实现复杂的逻辑判断。C 语言中的逻辑操作符包括:
-
逻辑与 (
&&):如果两个操作数都非零(即都为真),则条件成立(返回 1),否则条件不成立(返回 0)。 -
逻辑或 (
||):如果两个操作数中至少有一个非零(即至少有一个为真),则条件成立(返回 1),否则条件不成立(返回 0)。 -
逻辑非 (
!):用于反转操作数的逻辑状态。如果条件为真(非零),则逻辑非操作符将使其变为假(0);如果条件为假(0),则操作符将使其变为真(1)。
示例 1:使用逻辑与(&&)
#include <stdio.h>int main() {int age = 25;int score = 85;// 检查年龄是否在20到30之间,并且分数是否高于80if (age >= 20 && age <= 30 && score > 80) {printf("Candidate is selected.\n");} else {printf("Candidate is not selected.\n");}return 0;
}
示例 2:使用逻辑或(||)
#include <stdio.h>int main() {float temperature = 35.5;int isRaining = 1; // 用 1 表示下雨,0 表示没有下雨// 检查是否过热或下雨if (temperature > 30 || isRaining) {printf("It's not a good day for a picnic.\n");} else {printf("It's a good day for a picnic.\n");}return 0;
}
示例 3:使用逻辑非(!)
#include <stdio.h>int main() {int doorClosed = 1; // 用 1 表示门关闭,0 表示门开启// 检查门是否开启if (!doorClosed) {printf("The door is open.\n");} else {printf("The door is closed.\n");}return 0;
}
注意
在逻辑表达式中,C 语言采用短路求值(short-circuit evaluation):
- 在使用
&&操作符的表达式中,如果第一个操作数的值为假(0),则不会评估第二个操作数,因为整个表达式的结果已确定为假(0)。- 在使用
||操作符的表达式中,如果第一个操作数的值为真(非0),则不会评估第二个操作数,因为整个表达式的结果已确定为真(1)。这种行为可以用来防止执行可能导致错误的操作,例如在访问数组元素或指针之前先检查它们是否有效。
🍒位操作符
- 按位与 (
&): 对两个操作数中相对应的位执行逻辑与操作。 - 按位或 (
|): 对两个操作数中相对应的位执行逻辑或操作。 - 按位异或 (
^): 对两个操作数中相对应的位执行逻辑异或操作。 - 按位取反 (
~): 对操作数的每一位执行逻辑取反操作。 - 左移 (
<<): 把左操作数的位向左边移动指定的位数。 - 右移 (
>>): 把左操作数的位向右边移动指定的位数。
#include <stdio.h>int main() {unsigned int a = 5; // 二进制: 0101unsigned int b = 9; // 二进制: 1001int result;// 按位与result = a & b; // 二进制: 0001, 十进制: 1printf("a & b = %d\n", result);// 按位或result = a | b; // 二进制: 1101, 十进制: 13printf("a | b = %d\n", result);// 按位异或result = a ^ b; // 二进制: 1100, 十进制: 12printf("a ^ b = %d\n", result);// 按位取反result = ~a;// 注意:由于result是int类型,按位取反的结果将依赖于int的位数(假设是32位,实际的输出会是补码形式的一个负数)printf("~a = %d\n", result);// 左移result = a << 1; // 二进制: 1010, 十进制: 10printf("a << 1 = %d\n", result);// 右移result = a >> 1; // 二进制: 0010, 十进制: 2printf("a >> 1 = %d\n", result);return 0;
}//a& b = 1
//a | b = 13
//a ^ b = 12
//~a = -6
//a << 1 = 10
//a >> 1 = 2
🍒赋值操作符
在 C 语言中,赋值操作符用于给变量赋值。最基本的赋值操作符是等号 (=),它将右侧表达式的值赋给左侧的变量。此外,C 语言还提供了一系列复合赋值操作符,这些操作符结合了算术操作和赋值操作,可以使代码更加简洁。
基本赋值操作符
=:将右侧表达式的值赋给左侧的变量。
复合赋值操作符
+=:加上后赋值,a += b等价于a = a + b。-=:减去后赋值,a -= b等价于a = a - b。*=:乘以后赋值,a *= b等价于a = a * b。/=:除以后赋值,a /= b等价于a = a / b。注意,除以零会导致运行时错误。%=:取模后赋值,a %= b等价于a = a % b。对于整数类型,表示取除法的余数。<<=:左移后赋值,a <<= b等价于a = a << b。表示将a的二进制表示向左移动b位。>>=:右移后赋值,a >>= b等价于a = a >> b。表示将a的二进制表示向右移动b位。&=:按位与后赋值,a &= b等价于a = a & b。只有在a和b对应位都为 1 时,结果的对应位才为 1。^=:按位异或后赋值,a ^= b等价于a = a ^ b。只有在a和b对应位不同时,结果的对应位才为 1。|=:按位或后赋值,a |= b等价于a = a | b。只要a或b的对应位中有一个为 1,结果的对应位就为 1。
#include <stdio.h>int main() {int a = 2;printf("Initial value of a: %d\n", a);// 加上后赋值a += 3; // 等价于 a = a + 3;printf("After a += 3: %d\n", a);// 减去后赋值a -= 1; // 等价于 a = a - 1;printf("After a -= 1: %d\n", a);// 乘以后赋值a *= 2; // 等价于 a = a * 2;printf("After a *= 2: %d\n", a);// 除以后赋值a /= 4; // 等价于 a = a / 4;printf("After a /= 4: %d\n", a);// 取模后赋值a %= 3; // 等价于 a = a % 3;printf("After a %%= 3: %d\n", a);// 左移后赋值a = 1; // Reset a for shift operationsa <<= 2; // 等价于 a = a << 2;printf("After a <<= 2: %d\n", a);// 右移后赋值a >>= 1; // 等价于 a = a >> 1;printf("After a >>= 1: %d\n", a);// 按位与后赋值a &= 5; // 等价于 a = a & 5; (5 in binary: 101) printf("After a &= 5: %d\n", a);// 按位异或后赋值a ^= 3; // 等价于 a = a ^ 3; (3 in binary: 011)printf("After a ^= 3: %d\n", a);// 按位或后赋值a |= 8; // 等价于 a = a | 8; (8 in binary: 1000)printf("After a |= 8: %d\n", a);return 0;
}//Initial value of a : 2
//After a += 3 : 5
//After a -= 1 : 4
//After a *= 2 : 8
//After a /= 4 : 2
//After a %= 3 : 2//a=1
//After a <<= 2 : 4
//After a >>= 1 : 2// 与 &
// 0010 2
// 0101 5
// 0000 0//After a &= 5 : 0//异或 ^
// 0000 0
// 0011 3 异或不同为1
// 0011//After a ^= 3 : 3// 或 |
// 0011 3
// 1000 8
// 1011 11 //After a |= 8 : 11
binary 二进制
🍒强制类型转换符
强制类型转换(Type Casting)允许程序员手动改变一个值的数据类型。
虽然 C 语言在执行表达式时会自动进行类型转换(称为隐式类型转换或自动类型转换),但在某些情况下,程序员需要显式地指定类型转换,以避免自动转换可能带来的精度损失或类型不匹配的问题。
(目标类型) 表达式
示例 1:整数与浮点数之间的转换
将整数 i 强制转换为浮点数,以便在进行除法操作时保留小数部分,避免整数除法的截断行为。
#include <stdio.h>int main() {int i = 10;float f;// 将整数转换为浮点数进行除法操作以保持小数部分f = (float)i / 3;printf("The result of (float)i / 3 is: %f\n", f);return 0;
}//The result of i / 3 is: 3.000000
//The result of (float)i / 3 is: 3.333333
示例 2:字符类型与整数之间的转换
强制类型转换处理字符的 ASCII 值,以及如何将整数转换回字符类型。
#include <stdio.h>int main() {char c = 'A';int x;// 将字符转换为其 ASCII 码的整数值x = (int)c;// 将整数值转换回字符c = (char)(x + 1);printf("Original character was 'A', next character is: '%c'\n", c);return 0;
}
//Original character was 'A', next character is: 'B'
示例 3:提高表达式的计算精度
i1 被强制转换为 double 类型,以保证除法操作的精度,从而避免因为都是整数而导致的结果自动向下取整。
#include <stdio.h>int main() {int i1 = 2, i2 = 3;double result;// 通过强制类型转换确保结果的精度result = (double)i1 / i2;printf("The precise result of 2 / 3 is: %lf\n", result);//The precise result of 2 / 3 is: 0.666667return 0;
}
因为
i1被强制转换为double类型,而i2仍然是int类型,根据 C 语言的类型提升规则,i2也会被隐式地转换为double类型来执行运算。
示例 4:大小不同的数据类型之间的转换
将一个较大的整数值强制转换为一个字符。由于字符类型的存储空间较小,这样的转换可能会导致数据的丢失。这里 1024 转换为字符后的结果依赖于其在字符类型能表示的范围内的值。
#include <stdio.h>int main() {int i = 1024;char c;// 整数转换为字符,可能会丢失数据c = (char)i; // 只会保留 i 的低8位printf("The character representation of 1024 is: '%c'\n", c);return 0;
}
- 整数到字符的转换:
- 通过
(char)i强制类型转换,整数1024被转换为char类型。这种转换只保留整数值的低 8 位
- 数据的“丢失”:
- 因为
1024(二进制表示为100 0000 0000)转换为 8 位意味着只保留低 8 位,所以实际上你得到的是0(二进制的0000 0000)
- 输出结果:
- 输出的字符实际上是 ASCII 值为
0的字符,这是一个控制字符(NULL 字符),在控制台上不会显示为可见字符。
🍒成员访问操作符
- 使用
.操作符来访问一个结构体变量的成员。 - 使用
->操作符来访问一个指向结构体的指针所指向的成员。
使用.操作符
当你有一个结构体变量时,可以使用.操作符直接访问其成员。假设有一个结构体Person,其中包含两个成员:name和age。
#include <stdio.h>typedef struct {char* name;int age;
} Person;int main() {Person person;person.name = "Alice";person.age = 30;printf("%s is %d years old.\n", person.name, person.age);return 0;
}
在这个例子中,person是一个Person类型的变量。通过使用.操作符(例如person.name和person.age),可以访问和修改这个结构体的成员。
使用->操作符
当你有一个指向结构体的指针时,应该使用->操作符来访问其成员。继续使用上面的Person结构体为例,但这次我们将使用一个指针来访问它。
#include <stdio.h>typedef struct {char* name;int age;
} Person;int main() {Person person;Person *ptr = &person;ptr->name = "Bob";ptr->age = 25;printf("%s is %d years old.\n", ptr->name, ptr->age);return 0;
}
在这个例子中,ptr是一个指向Person类型的指针。通过使用->操作符(例如ptr->name和ptr->age),可以访问和修改指针所指向的结构体的成员。
🔥操作符优先级
在 C 语言中,不同的操作符有不同的优先级。这意味着,如果在一个表达式中同时使用了多个操作符,那么操作符的优先级将决定了这些操作符的执行顺序。以下是 C 语言中各种操作符的优先级列表,从高到低:
-
圆括号
():最高优先级,用于改变默认的优先级顺序。 -
单目操作符:如递增 (
++), 递减 (--), 逻辑非 (!), 取反 (~), 正负号 (+,-), 引用 (&), 解引用 (*), sizeof 等。 -
算术操作符:乘 (
*), 除 (/), 取模 (%), 加 (+), 减 (-)。其中,乘、除、取模的优先级高于加、减。 -
移位操作符:左移 (
<<), 右移 (>>). -
关系操作符:小于 (
<), 小于等于 (<=), 大于 (>), 大于等于 (>=), 等于 (==), 不等于 (!=). 其中,小于、小于等于、大于、大于等于的优先级高于等于、不等于。 -
位操作符:按位与 (
&), 按位异或 (^), 按位或 (|). -
逻辑操作符:逻辑与 (
&&), 逻辑或 (||). -
条件操作符(三元操作符):
? : -
赋值操作符:
=,以及复合赋值操作符如+=,-=,*=,/=,%=,<<=,>>=,&=,^=,|=。 -
逗号操作符:
, -
算术操作符优先级
int a = 2;
int b = 3;
int c = a + b * 2; // 首先计算 b * 2,然后将结果加上 a,所以 c 的值是 8,而不是 10。
- 关系操作符和逻辑操作符
int a = 2;
int b = 3;
int c = 4;
int result = a < b && b < c; // 首先计算 a < b 和 b < c,然后将两个结果进行逻辑与操作,所以 result 的值是 1。
- 使用括号改变优先级
int a = 2;
int b = 3;
int c = (a + b) * 2; // 首先计算括号内的 a + b,然后将结果乘以 2,所以 c 的值是 10,而不是 8。
- 赋值操作符和算术操作符
int a = 2;
int b = 3;
a *= b + 2; // 首先计算 b + 2,然后将结果乘以 a,所以 a 的新值是 10。
- 位操作符和关系操作符
int a = 2; // 二进制表示为 0010
int b = 3; // 二进制表示为 0011
int result = a & b > 1; // 首先计算 a & b,然后将结果和 1 进行比较,所以 result 的值是 1。
- 移位操作符和算术操作符
int a = 1;
int b = a << 2 + 1; // 首先计算 2 + 1,然后将 a 左移得到的位数,所以 b 的值是 8。
- 三元操作符和算术操作符
int a = 2;
int b = 3;
int c = a > b ? a + b : a - b; // 首先计算 a > b,根据结果选择执行 a + b 还是 a - b,所以 c 的值是 -1。
- 解引用操作符和算术操作符
int a = 2;
int b = 3;
int *p = &a;
int c = *p + b; // 首先获取 p 指向的值,然后将结果加上 b,所以 c 的值是 5。
- 逻辑非操作符和关系操作符
int a = 2;
int b = 3;
int result = !a < b; // 首先计算 !a,然后将结果和 b 进行比较,所以 result 的值是 0。
这些例子应该能帮助你更好地理解操作符优先级在 C 语言中的应用。
计算大小符sizeof
-
计算变量的大小:使用
sizeof可以获取任意变量占用的内存大小。这不仅适用于基本数据类型,如int、float、double等,也适用于结构体、联合体和数组。int a; size_t size = sizeof(a); // 计算int类型变量a的大小 -
计算数据类型的大小:除了变量,
sizeof还可以直接用于数据类型,来获取该类型占用的内存大小。size_t intSize = sizeof(int); // 直接计算int类型的大小 -
计算数组的总大小:对于数组,
sizeof返回整个数组占用的内存大小。这对于计算数组中元素的数量特别有用,尤其是当数组的大小不是手动指定的时候。int arr[10]; size_t arrSize = sizeof(arr); // 计算整个数组的大小 -
计算数组单个元素的大小:结合数组的总大小和单个元素的大小,可以计算出数组中元素的总数。
int arr[10]; size_t numElements = sizeof(arr) / sizeof(arr[0]); // 计算数组中元素的数量 -
计算结构体和联合体的大小:
sizeof同样适用于结构体和联合体,用于计算它们所占用的内存大小。这对于动态内存分配非常有用。struct MyStruct {int a;double b; }; size_t myStructSize = sizeof(struct MyStruct); // 计算结构体的大小 -
在动态内存分配中的应用:在使用
malloc或calloc进行动态内存分配时,sizeof操作符常用于指定需要分配的内存大小。int *p = (int *)malloc(sizeof(int) * 10); // 分配10个int大小的内存 -
类型安全的内存分配:使用
sizeof还可以增强代码的类型安全性,避免在内存分配时出现类型错误。int *p = malloc(sizeof(*p) * 10); // 使用*p来确定分配的类型和大小,更安全
条件操作符 ? :
条件操作符 ? :,也称为三元操作符,它允许在单个表达式内进行简单的条件判断。基本语法是:
condition ? expression1 : expression2;
这里的condition是一个布尔表达式。如果condition为真(非0值),则整个条件表达式的结果是expression1的值;如果为假(0值),则结果是expression2的值。
示例 1: 基础用法
int score = 75;
const char *result = score >= 60 ? "Passed" : "Failed";
printf("The student has %s the exam.", result);
在这个例子中,如果score大于或等于60,result将被赋值为"Passed";否则,result的值将是"Failed"。
示例 2: 嵌套条件操作符
三元操作符也可以嵌套,以处理更复杂的条件。假设我们现在除了判断是否通过外,还要区分优秀(分数大于等于85分):
int score = 90;
const char *result = score >= 85 ? "Excellent" : score >= 60 ? "Passed" : "Failed";
printf("The student's performance was: %s.", result);
这里,第一个条件判断是否score >= 85,如果是,结果是"Excellent"。如果不是,就会进行下一个判断:score >= 60,如果这个条件为真,结果是"Passed",否则是"Failed"。
const char *
- 初始化和赋值
char *用于可变字符串:
当你需要一个可修改的字符串时,可以使用char *配合动态内存分配或使用字符数组。
- 使用动态内存分配:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main() {char *str = (char *)malloc(20); // 分配 20 个字节的内存空间strcpy_s(str, 20, "Hello"); // 初始化字符串,安全地复制strcat_s(str, 20, ", world!"); // 修改字符串,安全地连接printf("%s\n", str); // 打印字符串free(str); // 释放内存return 0;
}
错误 C2440:“初始化”: 无法从“void *”转换为“char *”
在C++中(看起来您是在使用C++编译器编译C代码),
malloc返回的是void *类型,而C++是强类型语言,需要显式地将void *转换为其他类型的指针。因此,需要对malloc的返回值进行类型转换:char* str = malloc(20); // 错误 C2440:“初始化”: 无法从“void *”转换为“char *” char *str = (char *)malloc(20); // 强制转换 malloc 返回值为 char *
const char *用于不可变字符串:
当你仅需要访问或传递字符串,而不需要修改它时,应该使用const char *。
#include <stdio.h>int main() {const char *str = "Hello, world!";printf("%s\n", str); // 打印字符串// str[0] = 'h'; // 这将产生编译错误return 0;
}
这个例子中,str指向的字符串是不可修改的,任何尝试修改它的操作都会导致编译错误。
- 函数参数
使用char *作为函数参数允许函数修改字符串:
#include <stdio.h>
#include <string.h>void appendExclamationMark(char *str) {strcat(str, "!");
}int main() {char greeting[20] = "Hello, world";appendExclamationMark(greeting);printf("%s\n", greeting); // 输出 "Hello, world!"return 0;
}
使用const char *作为函数参数防止函数内部修改字符串:
#include <stdio.h>void printString(const char *str) {printf("%s\n", str);// str[0] = 'h'; // 这会产生编译错误
}int main() {const char *message = "Hello, world!";printString(message); // 安全地打印字符串,不担心被修改return 0;
}
按位取反
当你对一个无符号整数进行按位取反操作时,你实际上是将所有的位从0变为1,以及从1变为0。在C语言中,整数默认是以补码(two’s complement)形式表示的。补码是一种用二进制表示有符号整数的方法,使得负数的表示和加法运算都变得简单。下面是分析过程:
假设unsigned int a = 5;,且我们的系统中int类型是32位的。
-
原始的二进制表示:
- 在32位系统中,
a的完整32位二进制表示(包括前导零):00000000 00000000 00000000 00000101
- 在32位系统中,
-
进行按位取反操作:
- 对
a进行~a操作后的结果是将所有位翻转 变成11111111 11111111 11111111 11111010`。- 得到的是一个负数的补码(整数默认是以补码形式表示的)
- 对
-
理解补码表示:
- 在补码表示中,
11111111 11111111 11111111 11111010(或者取反+1) - 先减1:
11111111 11111111 11111111 11111001 - 取反码得到:
00000000 00000000 00000000 00000110(6的二进制表示) - 十进制
-611111111 11111111 11111111 11111010
- 在补码表示中,
因此,当你在代码中执行printf("~a = %d\n", result);后,输出会是-6,前提是int类型是32位的,并且我们假设系统使用补码表示负数。这个解释依赖于int类型具体的位数以及补码表示法,这两者在现代计算机系统中都是非常通用的假设。
snprintf字符串输出函数
snprintf 是一个在 用于格式化字符串输出的函数,属于 stdio.h 头文件。它的主要功能是将格式化的数据写入一个字符串中。
函数原型
int snprintf(char *str, size_t size, const char *format, ...);
str:目标字符串的指针,格式化后的输出将存储在这里。size:目标字符串缓冲区的最大长度。snprintf会确保不会向str写入超过size - 1个字符,最后一个位置留给空字符\0。format:格式字符串,控制后续参数如何格式化并插入到str。...:可变数量的额外参数,这些参数的类型和数量取决于格式字符串中的格式指定符。
返回值
- 成功时,返回欲写入的字符串长度(不包括空字符),即使这个长度大于
size,也不会实际写入超出size-1的内容。 - 出错时,返回负值。
使用示例
snprintf 将一个整数和一个浮点数格式化为一个字符串,存储在 buffer 中。同时,它还检查格式化后的字符串是否会超出缓冲区的大小,并相应地处理。
#include <stdio.h>int main() {char buffer[50];int a = 10;float b = 3.14159;// 使用 snprintf 将整数和浮点数格式化到字符串中int num_chars = snprintf(buffer, sizeof(buffer), "Integer: %d, Float: %.2f", a, b);if (num_chars >= 0 && num_chars < sizeof(buffer)) {printf("Formatted string: %s\n", buffer);} else if (num_chars >= sizeof(buffer)) {printf("Buffer too small, required %d characters.\n", num_chars);} else {printf("An error occurred.\n");}return 0;
}
细节
要修改代码以更清晰地表达其意图并解释相关的知识点,我们可以对原始代码进行一些调整。我们将重点放在:
- 缓冲区大小:确保有足够的空间存储整个字符串和末尾的空字符
\0。 snprintf的返回值:理解它表示的是尝试写入的字符数,不包括末尾的空字符。- 条件判断:清楚地处理各种情况,包括成功写入、缓冲区过小以及其他潜在错误。
#include <stdio.h>int main() {char buffer[3];char buffer2[4];// 使用 snprintf 将整数和浮点数格式化到字符串中int num_chars = snprintf(buffer, sizeof(buffer), "abc");int num_chars2 = snprintf(buffer2, sizeof(buffer2), "abc");printf("buffer1 %d \n", num_chars);printf("buffer1 %s \n", buffer);printf("buffer2 %d \n", num_chars2);printf("buffer2 %s \n", buffer2);return 0;}
//char buffer[3];
//buffer1 3
//buffer1 ab
//char buffer2[4];
//buffer2 3
//buffer2 abc