c语言笔记

c语言

第一个c程序

#include <stdio.h>
/*
 头文件
 函数声明和类型的定义
 */
/*
 入口函数:有且仅有一个
 */
int main(void)
{
	// 标识符:变量名,函数名,类型名.....
	// 命名规范:有字母数字下划线组成,数字不开头,区分大小写,避开c关键字

	printf("早上好!\n"); // 输出
    返回值正确返回输出的字符总数,错误返回负值
	return 0;// main不是mian 特殊接口,结束标志着进程的结束,return
	// return 值标志进程的终止状态
}

格式占位符

#include <stdio.h>

int main(void)
{
	// 存储数值---》定义相应类型的变量
	int num;// signed默认 unsigned(无符号)
	num = 1;
	/*
	 格式占位符:
	 	%d int
		%hd short
		%ld long
		%c char
		%p 地址
		%o 八进制
		%x 十六进制
		%10d 10输出数值所占宽度
	 */
	printf("num = %d\n", num);
	/*
	 转义字符
	 \n  	换行
	 \b  	退格
	 \r		回车(回到行首)
	 \t		水平制表符(缩进一个tap)
	 */
	printf("hello\bwo\trld\rhi\n");
	printf("%ld\n", sizeof(short));
	printf("%ld\n", sizeof(int));
	printf("%ld\n", sizeof(long));
	printf("%ld\n", sizeof(long long));

	printf("%ld\n", sizeof(float));
	printf("%ld\n", sizeof(double));
	printf("%ld\n", sizeof(long double));
	return 0;
}

C基本类型

变量:值可以随时改变

常量:只读

数据类型

  • 基本类型

    • 整形
      • short 2
      • int 4
      • long 8
      • long long 8
    • 字符类型
      • char 1
        • 存储单个字节
    • 实型
      • float 4
      • double 8
      • long double 16
  • 构造类型

    • 枚举
    • 结构体
    • 共用体
  • 指针类型

    • 存储地址类型
  • 空类型

    • void
  • 有符号和无符号

    • signed(默认)
    • unsigned
  • 类型转换

    • #include <stdio.h>
      
      int main(void)
      {
      	char ch1 = 'a';
      	int var;
      	float f;
      
      	var = ch1; // 自动类型强转(隐式转换)
      	          // 显式转换 (数据类型)变量
      
      	printf("var:%d\n", var);
      
      	printf("%d\n", var + ch1);
      
      	f = var + ch1;
      	printf("f:%f\n", f);
      
      	f = 1.9;
      	printf("%d\n", (int)f+1);//强转
      
      	return 0;
      }
      
      

变量定义

  • type name;
  • 标识符:变量名,函数名,类型名
    • 命名规范:由字母、数字、下划线组成,数字不开头,避开c关键字,区分大小写
    • "顾名思义"

初始化

  • type name = value; // 函数内变量未初始化,值是随机

运算符

   Operator                            Associativity   Notes
   () [] -> . ++ --                    left to right   [1]
   ! ~ ++ -- + - (type) * & sizeof     right to left   [2]
   * / %                               left to right
   + -                                 left to right
   << >>                               left to right
   < <= > >=                           left to right
   == !=                               left to right
   &                                   left to right
   ^                                   left to right
   |                                   left to right
   &&                                  left to right
   ||                                  left to right
   ?:                                  right to left
   = += -= *= /= %= <<= >>= &= ^= |=   right to left
   ,                                   left to right

控制语句

条件语句

  • if

    if (变量/表达式) {
    	// 循环体多条语句,一定要由{}
    }
    
  • if ... else

    if (变量/表达式) {
    	// 条件为真
    } else {
    	// 条件为假
    }
    
  • if... else if ....else if... else

    if (变量/表达式) {
    
    } else if (变量/表达式) {
    
    } else {
    
    }
    
  • switch

    switch(变量/常量表达式) {
    	case 常量值:
    		break;
    	case 常量值:
    		break;
    	default:
    		break;
    }
    

循环语句

  • for

  • for (表达式1; 表达式2; 表达式3) {
    	循环体语句4;
    }
    表达式1:循环变量初始化,只有进入循环执行一次
    表达式2:循环条件
    表达式3:循环变量的改变
    循环:1 2 4 3 2 4 3 
    都可缺省
    
  • while

  • while (条件) {
    	循环体语句;
    }
    
  • do .. while

  • do {
    	循环体;
    }while(条件);
    多用于错误校验
    
  • continue和break

    • continue 终止本次循环,继续下一次
    • break 终止最近循环
  • goto 无条件跳转

    • 标号ERROR:

函数

基本组成

功能实现封装到小的接口模块内

  • 定义

  • 定义:
    		返回值类型 函数名(参数列表)
    		{
    			函数体;
    		}
    
    
    • 函数名---》标识符 顾名思义
    • 返回值类型--》默认是int
    • 如果有返回值,return
  • 声明

    • 使用已经定义好的函数借口之前需要声明
    • 返回值类型 函数名(参数列表类型);
  • 调用

    • 函数名(参数)
    • 函数的传参---》值传递过程

变量类型

  • 局部变量:在函数体内定义的

    • 作用域与生存周期:从定义开始到函数结束
    • 未初始化值随机
  • 全局变量:在函数体外定义的

    • 作用域和生存周期:整个文件
    • 未初始化值0
  • 修饰变量关键字

    • 全局变量--->extern(默认) static

      • 定义在函数体外,整个程序
    • 局部变量---->auto(默认) static const register volatile

      • 定义在函数体内,函数内

      • static:

        • 修饰全局变量,限制其作用域在本文件中
      • 修饰函数,限制作用域在本文件中调用

        • 修饰局部变量,局部静态变量,延长生命周期到整个文件,但作用域不变
      • 只初始化一次

      • 未初始化值未0

      • const 保护变量

        • read-only 变量
      • register

        • 寄存器变量
      • volatile

        • 易失变量:防止编译优化

        • int num = 1;
          num == num; // 永远成立
          

        volatile int num = 1;
        num == num;// 不能确保永远成立

        
        
      • extern

        • 外部变量
    • 块变量:定义在语句块if for while语句块内, {}

#include <stdio.h>
extern int glob; // 全局变量, 未初始化值是0---->bss
static int glob2 = 2; // 全局变量, 数据段,static 限制的是作用域在本文件
static void test(void);
int main(void)
{
	auto int var; // 局部变量, 未初始化,值随机--->stack
	const int num = 5;
	num = 100;
	for (int i = 1; i < 10; i++)
		printf("i:%d\n", i); // 块变量, 栈
	test();
	test();
	test();
	test();
	return 0;
}
static void test(void) // 限制作用域在本文件
{
	static int n;
	printf("n:%d\n", n);
	n ++;
}

c存储空间布局

    • 局部变量,函数的形参
    • 未初始化,值随机值
    • 生命周期随着函数调用的结束而结束
    • 用户自行申请和释放
  • bss
    • 未初始化的局部静态变量和全局变量
    • 初始值为0
  • 数据段
    • 已经初始化的局部静态和全局变量
    • 生命周期都是从定义开始到进程结束
  • 文本段

特殊的函数

  • 递归函数

    • 在函数体内调用函数本身

      • 找到递归的终止条件

      • 找到递归点

      • #include <stdio.h>
        int fib(int n);
        int main(void)
        {
        	for (int i = 1; i < 20; i++) {
        		printf("%d ", fib(i));
        	}
        	printf("\n");
        
        	return 0;
        }
        /*
         终止条件
         递归点
         */
        int fib(int n)
        {
        	if (n == 1 || n == 2)
        		return 1;
        	if (n < 1)
        		return -1;
        
        	return fib(n-1) + fib(n-2);
        }
        
        
    • 递归用于解决复杂问题

      • 汉诺塔
      • 迷宫
  • 变参函数

    • ...
    • printf(3) / scanf(3)

  • define NAME value

  • 宏在gcc预处理阶段纯替换

  • 宏函数

    • 参数加括号

    • 末尾无分号

    • 建议do {}while(0)

    • /*
      #开头的---》预处理指令
      gcc四个步骤
      	1. 预处理:头文件的展开,宏的替换....  -E
      	2. 编译:翻译成汇编 -S
      	3. 汇编:生成目标文件 -c
      	4. 链接:动态链接(库函数)
       */
      #include <stdio.h>
      
      #define NUM 	10
      #define SQUARE(x)	(x)*(x)
      #define SWAP(x, y) \
      	do {\
      		typeof(x) t; t = x; x = y; y = t;\
      	}while(0)
      
      int main(void)
      {
      	int i;
      	int score;
      	int m, n;
      
      	printf("good morning\n");
      
      #if 0
      	for (i = 0; i < NUM; i++)
      		scanf("%d", &score);
      #endif
      	i = 5;
      	printf("%d\n", SQUARE(i+2)); // i+2*i+2
      
      	m = 10;
      	n = 20;
      	SWAP(m, n);
      	printf("m:%d, n:%d\n", m, n);
      	
      	return 0;
      }
      
    • 必须一行

    • 太长续行

    • x 将x替换为字符串

    • ‘##’ 连接

  • if 常量 / 常量表达式 #endif

  • ifdef 常量 #endif

  • ifndef xxx #endif

数组

基本知识

  1. 定义:相同类型元素的集合
    • type name[n];
  2. 赋值
    • 每一个成员变量通过下标值,从0开始
    • for (i = 0; i < n ; i++)
  3. 初始化
    • type name[n] = {}; 全部初始化为0
    • type name[n] = {1}; 第一个元素为1,其他为0
    • type name[] = {1,2,3}; 成员个数可缺省,大小取决于给定的值
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
/*
数组:相同类型元素的集合 
定义:type name[nmembs];
赋值: name[0],name[1], name[nmembs-1];
初始化:定义的同时赋值
	type name[nmembs] = {};
遍历:访问每一个元素,同赋值
 */
int main(void)
{
	int arr[5]; // 定义
	int i;
	int arr2[3] = {1,3}; // 初始化
    
	// srand(time(NULL));
	srand(getpid());
	// 赋值
	for (i = 0; i < 5; i++) {
		arr[i] = rand() % 100;
	}
	// 遍历
	for (i = 0; i < 5; i++)
		printf("%d ", arr[i]);
	printf("\n");
	return 0;
}

数组名

  • 数组首地址---》首元素的地址
  • 常量值
  • type *
  • 类型决定了步长+取值字节大小(sizeof(type))
    • int*步长---》sizeof(int)

只有当数组名在表达式中使用时,编译器才会为它产生一个指针常量。而只有以下两种情况,才不被当做指针常量:

  • sizeof(数组名):返回数组长度(所占的字节数,不是数组元素个数),而不是指向数组的指针的长度。
  • &数组名:产生一个指向数组的指针,而不是一个指向某个指针常量的指针。

运算符

  • a[b]--->*(a+b)--->b[a]
  • 取数组中的元素
    • name[index]
    • *(name+index)
  • 元素地址
    • name + index
    • &name[index];// &*(name+i)
    • *&抵消
    • 数组索引值[0,n-1],一定不要越界!!!!

排序

  1. 冒泡排序
    • 相邻元素两两比较,不符合大小顺序则交换
    • 每比较一整趟,使得一个元素有序。如果n元素,则比较n-1趟
      • for (i = 0; i < n-1; i++)
    • 每一趟比较的都是无序序列中的所有元素
      • for (j = 0; j < n-i-1; j++)
  2. 选择
    • 依次选择待排序的位置,该位置的元素与所有无序序列中的而元素一一比较,不符合大小关系则交换
      • for (i = 0; i < n-1; i++)
      • for (j = i+1; j < n; j++)
  3. 直接插入排序
    • 将所有元素分有序区和无序区,从无序序列中依次选择每一个元素,向有序序列中插入
    • 无序区中选带插入的元素
      • for (i = 1; i < n; i++)
    • 有序区待比较的元素
      • for (j = i-1; j >= 0; j --)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

#define N	10
int main(void)
{
	int arr[N] = {};
	int i, j;
	int t;
	int max;
	srand(getpid());
	for (i = 0; i < N; i++) {
		arr[i] = rand() % 100;
		printf("%d ", arr[i]);
	}
	printf("\n");
	// 冒泡排序
	for (i = 0; i < N-1; i++) {
		// 每一趟要比较的元素的范围
		for (j = 0; j < N-i-1; j++) {
			if (arr[j] > arr[j+1]) {
				t = arr[j];
				arr[j] = arr[j+1];
				arr[j+1] = t;
			}
		}
	}
	// 选择排序
	for (i = 0; i < N-1; i++) {
		// arr[i]选择的
		max = i;
		for (j = i+1; j < N; j++) {
			if (arr[j] > arr[max])	
				max = j;
		}
		if (max != i) {
			t = arr[i];
			arr[i] = arr[max];
			arr[max] = t;
		}
	}
	// 直接插入排序
	for (i = 1; i < N; i++) {
		t = arr[i];
		for (j = i-1; j >= 0; j--) {
			if (t < arr[j]) {
				arr[j+1] = arr[j];	
			} else 
				break;
		}
		// j < 0 || break
		arr[j+1] = t;
	}
	for (i = 0; i < N; i++) {
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}


查找

​ 有序数组---》二分查找(折半查找)

产生随机数

  • ​ rand(3)

  • ​ srand(3)

  • ​ 提供种子:time(2)---》获取时间戳 / getpid(2)

字符数组和字符串

c语言中没有字符串类型,用字符数组来存储字符串

  1. 字符串:多个字符组成并由'\0'作为结束标志

  2. 字符串初始化数组

    • char str[3] = "hi";
    • char str[] = "hi";
    • char str[3] = {'h', 'i', '\0'};

    字符函数

    strcpy 复制

    strcat 链接

    strcmp 比较

    strlen 返回字符串长度

多维数组

二维数组:理解为多个一维数组组成

  1. 定义

    type arr[一维数组个数][每一个一维数组的成员个数];
    int arr[2][3];由两个一维数组组成,每一个一维数组中有3个整形变量组成
    arr--->int *[3] 
    arr[0]-->int *
    
  2. 初始化

    int arr[2][3] = {{1,2,3}, {4,5,6}};
    
    arr[0] == &arr[0][0];
    arr[1] == &arr[1][0];
    
    *arr+4 == &arr[1][1]  == arr[1] + 1 
    
#include <stdio.h>
#include <string.h>
int main()
{
    int s[2][3] = {{9,8,7}, {3,2,1}}, (*p)[3];
	p = s;	//  s int *[3]
    //printf("&p = %p\n", &p);
    printf("s = %p\n", s); 
    printf("p+1 = %p\n", p+1); // 因为 p = s 相当于s的地址加三 到第二行
    printf("p = %p\n", p);
    printf("&p = %p\n", &p); //p的地址
    printf("*p = %p\n", *p); // *p对p的地址取值

    printf("**(p+1) = %d\n", **(p+1));
    printf("**s = %d\n", **(p+1));
    printf("**(p+1)= %d\n", **(p+1));
    printf("**p+1= %d\n", **p+1);
    printf("*(*s+4) = %d\n", *(*s+4)); // *s+4 == &s[1][1]  == sr[1] + 1 
    printf("*(*p+4) = %d\n", *(*p+4));
    printf("**p = %d\n", **p);
}

s = 000000000061FE00
p+1 = 000000000061FE0C
p = 000000000061FE00   // 和s 的地址相等
&p = 000000000061FDF8  // p 本身的地址
*p = 000000000061FE00  //对本身的地址取*操作得到p存放的值
**(p+1) = 3
**s = 3
**(p+1)= 3
**p+1= 10
*(*s+4) = 2
*(*p+4) = 2
**p = 9

指针

存储变量的地址 指针只能存地址!!!

定义

  1. type *name;

    char *p = NULL;  // 变量p 类型char * 
    //如果不确定p的指向暂时赋为 NULL(空)
    

类型

  1. 步长 取值大小
    1. *printf("%p\n", p+1); // char 指针+1---> 加sizeof(char)个字节
  2. 存地址---》变量大小8字节
#include <stdio.h>

int main(void)
{
	char str[] = "hello world";
	char *p = str; // 变量p 类型char *
	int arr[5] = {1,2,3,4,5};
	int *q;
	q = arr;
    
	printf("%ld\n", sizeof(p));
	printf("%p\n", p);
    
	printf("%p\n", p+1); // char *指针+1--->sizeof(char)
    
	while (*p) {
		printf("%c", *p);// p-->char *  (*p)-->取
		p++;
	}
	printf("\n");
	printf("%ld\n", sizeof(q));
	printf("%p\n", q);
	printf("%p\n", q+1);
	printf("%d\n", *q);
	return 0;
}

指针常量和常量指针const

#include <stdio.h>

/*
 指针常量
 常量指针
 */
int main(void)
{
	char str[] = "hello everyone";
	// 指针指向的是常量(*p),指针所指向的地址空间内容只读的
	const char *p = str; // 常量(的)指针
	char const *m = str; // 同上
	char *const q = str; // 指针(的)常量
	const char *const q = str // *q 和 q 都没改变
	str[0] = 'm';
	printf("%s\n", str);
	// *p = 'h'; 错误 *p 是常量不能改变
	p++;

	// q++;  错误 q 是常量不能改变
	*q = 'h';
    
	printf("%s\n", str);
	return 0;
}

用途

  1. 形参通过得到实参地址---》改变实参
  2. 可以通过参数返回值---》回填
  3. 数组,函数可以作为形参
#include <stdio.h>
/*
 指针的作用:
 1. 通过形参改变实参---》指针
 */
void swap2num1(int a, int b);
void swap2num(int *a, int *b);
int maxMinArr(int *arr, int n, int *min);
int main(void)
{
	int num1, num2;
	int arr[] = {3,2,7,6,8,9,4};
	int max, min;

	num1 = 100;
	num2 = 50;

	swap2num(&num1, &num2);
    //swap2num1(num1, num2 );
	printf("num1:%d, num2:%d\n", num1, num2);

	max = maxMinArr(arr, sizeof(arr) / sizeof(*arr), &min);
	printf("最大值:%d, 最小值:%d\n", max, min);

	return 0;
}

/*两个整型数的交换*/
void swap2num(int *a, int *b)
{
	int t;	
	
	t = *a; // t = num1
	*a = *b; // num1 = num2
	*b = t; // num2 = t
    
}
void swap2num1(int a, int b)
{
	int t;	
	
	t = a; // t = num1
	a = b; // num1 = num2
	b = t; // num2 = t
    
}
/*
 将给定的数组的最大最小值返回
 参数的回填
 */
int maxMinArr(int *arr, int n, int *min)
{
	int i;		
	int maxn, minn;

	maxn = minn = arr[0];
	for (i = 1; i < n; i++) {
		if (arr[i] > maxn) {
			maxn = arr[i];
		}
		if (arr[i] < minn)
			minn = arr[i];
	}
    *min = minn;

	return maxn;
}

指针操作

#include <stdio.h>
#define MONTHS 12


int main(void)
{
    int days[MONTHS] = {31,28,31,30,31,30,31,31,30,31,30,31};
    int index;
    int *p = days;
    //days+1 返回的是整个表达式的值,而 days 还是那个常量 days
    printf("Month %2d has %d days.\n", index + 1, *days+1);
     
    //days++;  //错误 days 的值不可修改 数组名是常量;
    //是要把 days 这个常量自增,即加1,那肯定不行,要报错;
    for (index = 0; index < MONTHS; index++)
        printf("Month %2d has %d days.\n", index + 1, *(days + index));
    return 0;
}

#include <stdio.h>

int main(void)
{
	// !!!!!!!!指针只能存地址
	char str[] = "good morning";

	char *q = str;

	char *p;
	p = q;
	
    // ++在后先运算在++
	printf("%c\n", *p++); // 'g' arg = *p++; 先对*p做运算在对p++  printf()就相当于运算把*p的值打印出来
	printf("%c\n", (*p)++); // 'o' arg = (*p)++ "gpod moring" arg==>'o'  先对*p做运算在对(*p)做++ 运算; (*p)++ = (*p)+ 1 = 'o' + 1 = p'
	printf("%c\n", *(p++)); // 'p' arg = *(p++) arg = *p; p++ 先取*p 在p++
	printf("%c\n", *++p); // 'd'  先++p 在取*
	printf("%c\n", ++*p); // 'e'  先++*p ==> *p = 1 + *p 在取*p

	puts(str);
    puts(p-3);
    
	return 0;
}
#include<stdio.h>

int main(void)
{
    int urn[5] = {100, 200, 300, 400, 500};
    int *ptr1, *ptr2, *ptr3;

    ptr1 = urn;
    ptr2 = &urn[2];

    printf("ptr1 = %p, *ptr1 = %d, &ptr1 = %p\n",ptr1, *ptr1, &ptr1);
    //指针加法
    ptr3 = ptr1 + 4;
    printf("ptr3 = %p, ptr1 + 4 = %p, *(ptr1 + 4) = %d\n",ptr3, ptr1 +4 , *(ptr1 + 4));

    ptr1++; //递增指针
    printf("ptr1 = %p, *ptr = %d, &ptr1 = %p\n",ptr1, *ptr1, &ptr1);
    ptr2--; //递减指针
    printf("ptr2 = %p, *ptr2 = %d, &ptr2 = %d\n",ptr2, *ptr2, &ptr2);
    --ptr1; //恢复初始值
    ++ptr2; //恢复初始值
    printf("ptr1 = %p, ptr2 = %p\n",ptr1, ptr2);  
    // 一个指针减去另一个指针
    printf("ptr2 = %p, ptr1 = %p, ptr2 - ptr1 = %td\n",ptr2, ptr1, ptr2 - ptr1);
    //一个指针减去一个整数
    printf("ptr3 = %p ,ptr3 - 2 = %p\n", ptr3, ptr3 - 2);
}




ptr1 = 000000000061FE00, *ptr1 = 100, &ptr1 = 000000000061FDF8
ptr3 = 000000000061FE10, ptr1 + 4 = 000000000061FE10, *(ptr1 + 4) = 500
ptr1 = 000000000061FE04, *ptr = 200, &ptr1 = 000000000061FDF8
ptr2 = 000000000061FE04, *ptr2 = 200, &ptr2 = 6422000
ptr1 = 000000000061FE00, ptr2 = 000000000061FE08
ptr2 = 000000000061FE08, ptr1 = 000000000061FE00, ptr2 - ptr1 = 2
ptr3 = 000000000061FE10 ,ptr3 - 2 = 000000000061FE08

二级指针

1. 存储一级指针变量的地址
2. 用途
  	1. 存储指针数组首地址
  	2. 参数列表返回地址(堆)

数组指针

  1. int (*p)[3]; 存储的3个地址连续整型数的地址

  2. #include <stdio.h>
    
    int main(void)
    {
    	int arr[2][3] = {1,2,3,4,5,6};
    
    	// arr--->arr[0] arr[1]
    	// arr[0]--->{1,2,3}
    	// arr--->&arr[0]--->int *[3]
    	int (*p)[3]= arr; // 数组指针
    
    	printf("%p\n", p);
    	printf("%p\n", p+1);
    
    	printf("%d\n", p[0][2]);
    
    	return 0;
    }
    
    

指针数组

#include <stdio.h>

int main(int argc, char *argv[]/* char **argv */)
{
#if 0
	// 指针数组
	char *arr[4] = {"hello", "world", "good", "afternoon"};
	for (int i = 0; i < 4; i ++) {
		printf("%s\n", arr[i]);
	}
	// arr--->char **
	char **p = arr; // 二级指针
#endif

	for (int i = 0; i < argc; i++) {
		puts(argv[i]);
	}

	return 0;
}

动态开辟

  • 堆空间使用
    malloc(3)
    calloc(3)
    reallock(3)
    free(3);

void *万能指针类型
void 指针可以存储任意类型的变量地址,但没有步长,不能+- 也不能

memset(3); 存储空间初始化
memcpy(3); 存储空间复制
memmove(3); 功能同上

函数指针

int (*p)(int , int)

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void sortarr(void *arr, int nmemb, int size, int (*cmp)(const void *data1, const void *data2));

static int intcmp(const void *data1, const void *data2)
{
	const int *d1 = data1;
	const int *d2 = data2;

	return *d2 - *d1;
}

static int charcmp(const void *data1, const void *data2)
{
	const char *c1 = data1;
	const char *c2 = data2;

	return *c1 - *c2;
}

static int stringcmp(const void *data1, const void *data2)
{
	char *const*d1 = data1;
	char *const*d2 = data2;

	return strcmp(*d2, *d1);
}

int main(void)
{
	int arr[] = {3,2,1,6,7};
	char str[] = "hello world";
	char *sarr[] = {"hello", "world", "hi", "good", "girls", "boys"};
	double strrr[] = {4.33,5.66,7.00,9.66};
	sortarr(strrr,sizeof(strrr) / sizeof(*strrr),sizeof(double),intcmp);
	sortarr(arr, sizeof(arr) / sizeof(*arr), sizeof(int), intcmp);
	sortarr(str, strlen(str), sizeof(char), charcmp);
	sortarr(sarr, sizeof(sarr) / sizeof(*sarr), sizeof(char *), \
			stringcmp);

	for (int i = 0; i < sizeof(strrr) / sizeof(*strrr); i++)
		printf("%d ", strrr[i]);
	printf("\n");

	for (int i = 0; i < sizeof(arr) / sizeof(*arr); i++)
		printf("%d ", arr[i]);
	printf("\n");

	printf("%s\n", str);

	for (int i = 0; i < sizeof(sarr) / sizeof(*sarr); i++)
		printf("%s\n", sarr[i]);

	return 0;
}

// 任意类型的数组排序
// (char *)arr + size*i
void sortarr(void *arr, int nmemb, int size, int (*cmp)(const void *data1, const void *data2)) 
{
	int i, j;
	char *tmp;

	tmp = malloc(size);
	if (NULL == tmp)
		return;

	for (i = 0; i < nmemb-1; i++) {
		for (j = 0; j < nmemb-i-1; j++) {
			if (cmp((char *)arr+j*size, (char *)arr+(j+1)*size) < 0) {
				memcpy(tmp, (char *)arr+j*size, size);	
				memcpy((char *)arr+j*size, (char *)arr+(j+1)*size, size);
				memcpy((char *)arr+(j+1)*size, tmp, size);
			}
		}
	}
	free(tmp);
}



posted @ 2021-02-03 17:35  小浩small  阅读(130)  评论(1)    收藏  举报