一.函数
1.1定义函数的格式:
返回值类型 函数名(函数的参数列表){
函数体; //用来实现功能的代码块
}
1.2函数的参数:
形式参数:在定义函数时 ()里面的参数,简称“形参”。
实际参数:调用函数时 ()里面的参数,简称”实参“。
实参的个数、类型 都要和形参保持一致。
1.3函数的返回值
函数的返回值就是将函数执行的结果返回给函数调用使用,不需要返回值可以不写。
1.4函数传参方式
1.4.1复制传参(值传递)
把实参的值复制给形参,函数内对形参的任何操作都不会改变实参的的值
#include<stdio.h>
// 功能:将参数扩大100倍后求和
void user_add(int num1, int num2){
num1 *= 100;
num2 *= 100;
printf("%d\n",num1+num2);
}
int main(int argc, const char *argv[])
{
int a = 1;
int b = 2;
user_add(a, b); //300
return 0;
}
1.4.2地址传参(地址传递)
把指针变量所指向变量的地址复制给形参,如果函数里对指针变量所指向变量的值修改,实参也会被修改
#include <stdio.h>
int user_add(int x, int y, int *z){
printf("my_add : z = %p\n", z); // 和实参c的地址是一样的
*z = x+y;
}
int main(int argc, const char *argv[])
{
int a = 100;
int b = 200;
int c = 0;
printf("main : &c = %p\n", &c); // 和函数里的z是一样的
user_add(a, b, &c);
printf("c = %d\n", c); // 300
return 0;
}
注意:形参是指针也不一定地址传参,有可能是指针的复制传参。如下代码:
#include <stdio.h>
int m = 100;
int n = 200;
void func1(int *q){
q = &n;
}
void func2(int **q){
*q = &n;
}
int main(int argc, const char *argv[])
{
int *p = &m;
// 一级指针的值传递
func1(p);
printf("*p = %d\n", *p); // 100
// 一级指针的地址传递
func2(&p);
printf("*p = %d\n", *p); // 200
return 0;
}
1.5数组的传参方式
1.5.1字符串数组
字符串传参只需要传首地址,因为每个字符串结尾都有‘\0’来标识字符串结束。
#include<stdio.h>
char *user_strcpy(char *dest, const char*src){
char *temp = dest;
while(*src != '\0'){
*dest = *src;
dest++;
src++;
}
*dest = *src; //'\0'fu赋给目标字符串
return temp;
}
int main(int argc, const char *argv[])
{
char s1[10] = "hello";
char s2[10] = "1234";
user_strcpy(s1,s2);
printf("s1 = [%s] s2 = [%s]\n", s1, s2); // 1234
char s3[] = "world";
//用user_strcpy函数的返回值作为实参
user_strcpy(s1, user_strcpy(s2, s3));
printf("s1 = [%s] s2 = [%s] s3 = [%s]\n", s1, s2, s3); // world
return 0;
}
1.5.2整型数组
整型数组传参时,既要传首地址,还要传数组的长度。
include <stdio.h>
// 常用的写法----用指针做形参即可
void print_arr_1(int *p, int len){
for(int i = 0; i < len; i++){
printf("%d ", p[i]);
}
printf("\n");
}
// 这种写法叫做代码的自注释
// p 的本质还是指针 并不是一个长度为10的数组
//void print_arr_2(int p[10], int len){
void print_arr_2(int p[], int len){
printf("sizeof(p) = %ld\n", sizeof(p)); // 8
for(int i = 0; i < len; i++){
printf("%d ", p[i]);
}
printf("\n");
}
int main(int argc, const char *argv[])
{
int s[10] = {1,2,3,4,5,6,7,8,9,10};
print_arr_1(s, 10);
print_arr_1(s, 5);
print_arr_2(s, 10);
return 0;
}
1.5.3二维数组
二维数组作为实参时,形参需要用数组指针
#include <stdio.h>
void print_arr(int (*p)[4], int hang, int lie){
for(int i = 0; i < hang; i++){
for(int j = 0; j < lie; j++){
printf("%d ", p[i][j]);
}
printf("\n");
}
}
int main(int argc, const char *argv[])
{
int s[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
print_arr(s, 3, 4);
return 0;
}
1.6main函数的参数
int main(int argc, const char *argv[ ])
argc是执行程序时命令行的参数的个数(包括可执行文件名./a.out)
argv是一个指针,指向一个const cgar *类型的指针数组
数组排序,终端输入./a.out -升序,./a.out -降序
#include<stdio.h>
//封装排序的函数,flag=1升序,flag=0降序
int user_sort(int *p, int len, int flag){
int i = 0;
int j = 0;
int temp = 0;
if(flag){ //flag != 0
for(i = 0;i < len-1;i++){
for(j = 0;j < len-1-i;j++){
if(p[j] > p[j+1]){
temp = p[j];
p[j] = p[j+1];
p[j+1] = temp;
}
}
}
}else{
for(i = 0;i < len-1;i++){
for(j = 0;j < len-1-i;j++){
if(p[j] < p[j+1]){
temp = p[j];
p[j] = p[j+1];
p[j+1] = temp;
}
}
}
}
return 0;
}
int main(int argc, const char *argv[])
{
//入参合法性检查
if(2 != argc){
printf("Usage : %s -s/-j\n", argv[0]);
return -1;
}
int s[10] = {11, 23, 32, 54, 5, 1, 18, 96, 7, 9};
//if(!strcmp(argv[1]."-s")) 用strcmp比较字符串
//比较字符 if('s' == *(argv[1]+1)) argv[1]是命令行第二个字符串的地址,+1在1取*操作的是 -后面的字符
if('s' == *(*(argv+1)+1)){ //if('s' == argv[1][1])
user_sort(s, 10, 1);
}else{
user_sort(s, 10, 0);
}
//输出
for(int i = 0; i < 10; i++){
printf("%d ", s[i]);
}
printf("\n");
return 0;
}
2.函数与指针、数组
2.1指针函数
本质是一个函数,返回值是一个指针类型。
注意事项:
1.不能返回局部变量的地址,局部变量占用的内存在函数结束后被操作系统收回。
2. 可以返回:全局变量的地址、static修饰的局部变量的地址、通过参数传递的地址、
堆区手动分配的内存空间。
#include<stdio.h>
#include<stdlib.h>
int *func1(int x, int y){
int temp = x + y;
return &temp; //局部变量占用的内存在函数结束时就被操作系统回收
}
int value = 0;//全局变量
int *func2(int x, int y){
value = x + y;
return &value;
}
int *func3(int x, int y){
static int temp = 0; //static修饰
temp = x+y;
return &temp;
}
int *func4(int x, int y, int *z){ //地址传参
*z = x+y;
return z;
}
int main(int argc, const char *argv[])
{
int *p = NULL;
// p = func1(10, 20);\
printf("%d\n",*p); //结果不可预知,错误用法,不能返回局部变量地址
// p = func2(10, 20);\
printf("%d\n",*p); //30 可以返回全局变量地址
//p = func3(10, 20);
//printf("%d\n", *p); // 30 可以返回static修饰的局部变量地址
int num = 0;
p = func4(10, 20, &num);
printf("%d\n", *p); // 30 可以返回地址传参的地址
int *q = (int *)malloc(sizeof(int)); //指针指向堆区使用malloc函数分配4字节内存的地址
*q = 10 + 20;
printf("%d\n",*q); //30 可以返回堆区手动分配空间的地址
free(q);//释放
p = NULL; //防止误操作导致非法访问free,需将q置 NULL
return 0;
}
2.2函数指针
本质是个指针,指向一个函数
格式:返回值类型 (*函数指针名)(形参列表)
#include<stdio.h>
void user_swap(int *x, int *y){
*x = *x ^ *y;
*y = *x ^ *y;
*x = *x ^ *y;
}
int main(int argc, const char *argv[])
{
//定义一个函数指针p,指向一个返回值类型是void、形参列表是(int *,int *)类型的函数
void (*p)(int *x, int *y) = NULL;
p = user_swap; //函数名就是函数的首地址
//函数指针指向函数后,可以通过函数指针调用函数
int a = 10;
int b = 20;
user_swap(&a, &b);
printf("a=%d b=%d\n",a, b); //20 10
a = 10;
b = 20;
p(&a, &b);
printf("a=%d b=%d\n",a, b); //20 10
return 0;
}
函数指针的典型使用场景----用作回调函数
将函数指针p作为函数user的形参。在函数user内部通过函数指针去调用函数时,具体用哪个函数由用户在调用函数user时传递的第三个参数。第三个参数传递到哪个函数,p就调用哪个函数。次用法称为回调函数。
#include<stdio.h>
int my_add(int x, int y){
printf("%d\n", x+y);
}
int my_sub(int x, int y){
printf("%d\n", x-y);
}
int user(int x, int y, int (*p)(int, int)){
p(x, y);
}
int main(int argc, const char *argv[])
{
int a = 10;
int b = 20;
user(a, b, my_add);//30
user(a, b, my_sub);//-10
return 0;
}
2.3函数指针数组
返回值类型 (*函数指针数组名][下标](函数的参数列表)
int (*p[2])(int);
2.4函数指针数组指针
返回值类型 (*(*a)[下标])(函数的参数列表)
//int (*(*p)[2])(int);
3.字符串函数
3.1strlen
#include <string.h>
size_t strlen(const char *s);
参数:要计算长度的字符串的首地址
返回值:计算的结果
功能:计算字符串长度,第一个'\0'之前的字符的个数
sizeof 和 strlen 的区别:
strlen是函数调用 计算第一个'\0'之前的字符数
sizeof是一个C语言关键字 计算变量或者类型占用的内存空间的大小的
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{
char s1[20] = "hello world";
// 可以定义变量保存结果
int ret = strlen(s1);
printf("ret = %d\n", ret); // 11
// 也可以直接把结果打印到终端
printf("strlen(s1) = %ld\n", strlen(s1)); // 11
char s2[10] = "hqyj";
printf("strlen(s2) = %ld\n", strlen(s2)); // 4
// C语言对字符串处理时遇到\0就结束了
char s3[10] = "ab\0cd";
printf("strlen(s3) = %ld\n", strlen(s3)); // 2
//char s1[20] = "hello world";
printf("strlen(s1) = %ld\n", strlen(s1)); // 11
printf("sizeof(s1) = %ld\n", sizeof(s1)); // 20
return 0;
}
自定义函数
#include<stdio.h>
//定义一个计算字符串长度
int user_strlen(char *p){ //p是存字符串的数组名
#if 0
int count = 0; //定义一个count来计数
while(p[count] != 0){ // 0='\0' 计算第一个'\0'之前的字符的个数
count++;
}
#endif
#if 0
int count = 0;
while(p[count]){ // p[count]这个表达式的结果非0即可执行while循环
// 等价于while(s[i] != 0)
count++;
}
#endif
#if 1
int count = 0;
while(p[count++]); // p[count++])这个表达式的结果非0即可执行while循环,等价于上方代码
count--; // 当p[count++]等于'0'时,因为count++自增,count保存的数比实际数多加了一次1
//所以count需要自减1
#endif
return count;
}
int main(int argc, const char *argv[])
{
char arr[20] = "hello\0word";
int ret = user_strlen(arr);
printf("%d",ret);
return 0;
}
3.2strcpy
#include <string.h>
char *strcpy(char *dest, const char *src);
功能:字符串的拷贝,将src指向的字符串包括'\0'都拷贝给dest指向的空间
参数:
dest:目标字符串
src:源字符串
返回值:dest的首地址
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{
char s1[20] = "hello world";
char s2[200] = "beijing";
printf("拷贝前:s1 = [%s]\n", s1); // hello world
printf("拷贝前:s2 = [%s]\n", s2); // beijing
// 把s2拷贝给s1 要保证s1足够大!!!
strcpy(s1, s2);
printf("拷贝后:s1 = [%s]\n", s1); // beijing
printf("拷贝后:s2 = [%s]\n", s2); // beijing
// s1中剩下的字符 还在里面 只不过通过字符串的方式 访问不到了
printf("s1[7] = [%c] [%d]\n", s1[7], s1[7]); // 0
printf("s1[8] = [%c] [%d]\n", s1[8], s1[8]); // r
printf("s1[9] = [%c] [%d]\n", s1[9], s1[9]); // l
printf("s1[10] = [%c] [%d]\n", s1[10], s1[10]); // d
// strcpy函数也可以用来给字符串重新赋值
char s3[10] = "abcd";
//s3 = "hqyj";//错误的写法
strcpy(s3, "hqyj");
printf("s3 = [%s]\n", s3); // hqyj
return 0;
}
自定义函数
#include<stdio.h>
void user_strcpy(char *dest, char *src){
#if 0
int i = 0;
while(src[i]){ //等价于while(src[i] != '\0'){
dest[i] = src[i];
i++;
}
dest[i] = src[i]; // 等价于dest[i] = '\0',要把src中的'\0'复制给dest中
#endif
#if 0
int i = 0;
while(src[i]){ //while(dest[i++]=src[i++]);相当于i++进行了两次
dest[i++]=str[i]); //while(dest[i++]=src[i++]);相当于i++进行了两次
} //while(dest[i]=src[i++]);因为i++自增,相当于把src的第i位赋值给dest的i+1
dest[i] = src[i]; // 等价于dest[i] = '\0',要把src中的'\0'复制给dest中
#endif
int i = 0;
while(dest[i++]=str[i]); //代码先执行str给dest赋值,后执行while循环
} //已经把str中的'\0'赋值给了dest中,判断while循环结束
int main(int argc, const char *argv[])
{
char s1[20] = "hello world";
char s2[20] = "beijing";
printf("拷贝前:s1 = [%s]\n", s1); // hello world
printf("拷贝前:s2 = [%s]\n", s2); // beijing
user_strcpy(s1, s2); //函数调用
printf("拷贝后:s1 = [%s]\n", s1); // beijing
printf("拷贝后:s2 = [%s]\n", s2); // beijing
return 0;
}
int i = 0;
while(dest[i++]=str[i]); //while(dest[i++]=src[i++]);相当于i++进行了两次
} //while(dest[i]=src[i++]);因为i++自增,相当于把src的第i位赋值给dest的i+1
int main(int argc, const char *argv[])
{
char s1[20] = "hello world";
char s2[20] = "beijing";
printf("拷贝前:s1 = [%s]\n", s1); // hello world
printf("拷贝前:s2 = [%s]\n", s2); // beijing
user_strcpy(s1, s2); //函数调用
printf("拷贝后:s1 = [%s]\n", s1); // beijing
printf("拷贝后:s2 = [%s]\n", s2); // beijing
return 0;
3.3strcat
#include <string.h>
char *strcat(char *dest, const char *src);
功能:将src拼接到dest后面,会覆盖dest的'\0' 同样要注意保证dest足够大 否则会越界访问
参数:
dest:目标字符串
src:源字符串
返回值:dest的首地址
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{
char s1[20] = "hello world";
char s2[20] = "abcd";
printf("拼接前:s1 = [%s]\n", s1); // hello world
printf("拼接前:s2 = [%s]\n", s2); // abcd
// 把s2拼接到s1后面 要保证s1足够大!!!
strcat(s1, s2);
printf("拼接后:s1 = [%s]\n", s1); // hello worldabcd
printf("拼接后:s2 = [%s]\n", s2); // abcd
return 0;
}
自定义函数:
#include <stdio.h>
#include <string.h>
void user_strcat(char *dest, char *src){
#if 0
int i = 0;
while(dest[i] != '\0'){ //先找到dest的'\0'的下标
i++;
}
//拼接
int j = 0;
while(src[j] != '0'){
dest[i] = src[j];
i++;
j++;
}
dest[i] = src[j]; //src的'\0'需要追加给dest
}
#else
int i = 0;
while(dest[i++]); //因为i++自增 i的值是'\0'后一位的下标
i--; //i需要减1
int j = 0;
while(dest[i++] = src[j++]);
//先赋值,后循环,当src[i]='\0'时,while结束循环时,src的\0已经赋值给dest
#endif
}
int main(int argc, const char *argv[])
{
char s1[20] = "hello world";
char s2[20] = "abcd";
printf("拼接前:s1 = [%s]\n", s1); // hello world
printf("拼接前:s2 = [%s]\n", s2); // abcd
// 把s2拼接到s1后面 要保证s1足够大!!!
user_strcat(s1, s2);
printf("拼接后:s1 = [%s]\n", s1); // hello worldabcd
printf("拼接后:s2 = [%s]\n", s2); // abcd
return 0;
}
3.4strcmp
#include <string.h>
int strcmp(const char *s1, const char *s2);
功能:比较两个字符串,并不是比较长度,而是逐个的比较两个字符串中对应字符的ascii码,直到出现大小关系时,立即返回。只有两个字符串中第一次出现'\0'之前的所有字符都相等 才认为两
字符串相等
返回值:
<0 s1<s2
>0 s1>s2
0 s1==s2
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[])
{
char s1[10] = "abcdefg";
char s2[10] = "abcdxfg";
//需要看返回值 来确定到底哪个大
int ret = strcmp(s1, s2);
if(0 == ret){
printf("s1 == s2\n");
}else if(ret > 0){
printf("s1 > s2\n");
}else if(ret < 0){
pr
因为 'x' > 'e'
}
return 0;
}
自定义函数:
#include<stdio.h>
//定义一个比较的函数
int user_strcmp(char *arr1, char *arr2){
int i = 0;
while(arr1[i] != '\0' && arr2[i] != '\0'){
if(arr1[i] != arr2[i]){
break;
}
i++;
}
return arr1[i] - arr2[i];
}
int main(int argc, const char *argv[])
{
char arr1[10] = "abcd";
char arr2[10] = "abcdefg";
//需要看返回值 来确定到底哪个大
int ret = user_strcmp(arr1, arr2);
if(0 == ret){
printf("arr1 == arr2\n");
}else if(ret > 0){
printf("arr1 > arr2\n");
}else if(ret < 0){
printf("arr1 < arr2\n"); // 上面例子中 arr2 更大 因为 'x' >
}
printf("ret = %d\n", ret);
return 0;
}
strcmp常用于判断两个字符是否相等
#include<stdio.h>
#include<string.h> //strcmp的头文件
int main(int argc, const char *argv[])
{
char sport[10] = {0};
printf("请输入是否喜欢运动:");
scanf("%s",sport);
//用strcmp返回值来判断两个字符串是否相等
if(!strcmp(sport, "喜欢")){ //等价于if(0 == strcmp(sport, "喜欢" ))
printf("用户输入的是喜欢\n");
}else{
printf("用户输入的是不喜欢\n");
}
return 0;
}
3.5字符串处理
3.5.1字符串转整型
#include <stdlib.h>
int atoi(const char *nptr);
功能:将字符串转换成整型
参数:字符串
返回值:转换后的整型
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
char s[10] = "1234";
int num = atoi(s);
printf("num = %d\n", num); // 1234
return 0;
}
自定义函数:
#include<stdio.h>
int array_to_integer(char *p){
int i = 0;
int num = 0;
while(p[i] != '\0'){
num *= 10;
num += p[i] - '0';
i++;
}
return num;
}
int main(int argc, const char *argv[])
{
int num = 0;
char arr[10] = "1234";
num = array_to_integer(arr);
printf("%d\n",num); //1234
return 0;
}
3.5.2字符串翻转
例如输入:hello world beijing
则输出:gnijieb dlrow olleh
#include<stdio.h>
int main(int argc, const char *argv[])
{
//终端获取字符串
char arr[100] = {0};
gets(arr);
int i = 0;
int temp = 0;
//遍历数组有几个字符
while(arr[i++]);
i -= 2;//上面循环结束时i是\0后一位的下标
//字符串翻转
int j = 0;
while(j < i){
temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
j++;
i--;
}
printf("%s\n",arr);
return 0;
}
中文字符串翻转
输入一个中文字符串翻转(在linux系统中一个中文占3个字节)
输入“我爱中国” 输出“国中爱我”
#include<stdio.h>
int main(int argc, const char *argv[])
{
char arr[100] = {0};
gets(arr);
//遍历字符转数组,找到最后一个中文的起点
int i = 0;
while(arr[i++]);
i -= 4; //定位到最后一个中文的起点
char temp = 0;
int j = 0;
while(j < i){
for(int k = 0;k < 3;k++){
temp = arr[j + k];
arr[j + k] = arr[i + k];
arr[i + k] = temp;
}
j += 3;
i -= 3;
}
printf("%s",arr);
return 0;
}
英文字符串翻转
输入"i love china too" 输出"too china love i "
#include<stdio.h>
int main(int argc, const char *argv[])
{
int i = 0;
int j = 0;
int k = 0;
int temp = 0;
char arr[100] = {0};
gets(arr); //终端获取一个字符串
//遍历数组有多少个字符
while(arr[i++]);
i -= 2;
//所有的字符翻转
while(j < i){
temp = arr[i];
arr[i] = arr[j];
arr[j]= temp;
j++;
i--;
}
//清零
i = 0;
j = 0;
while(1){
//遍历单词
while(arr[i] != 32 && arr[i] != 0){ //32是空格的assic码 0是'\0'的assic码
i++;
}
//定义变量k来保存下一个单词第一位的下标
k = i + 1; //此时i是第一个单词后的空格下标,需+1
i--; //i-1后 i才是第一个单词的最后一位下标
while(j < i){
temp = arr[i];
arr[i] = arr[j];
arr[j]= temp;
j++;
i--;
}
//i和j此时都是下一个单词的第一位字母下标
i = k;
j = k;
if(!arr[i]){ //等价于 if(arr[i] == '\0')
break;
}
}
printf("%s",arr);
return 0;
}