初始c笔记bing
Posted 鸟随二月
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了初始c笔记bing相关的知识,希望对你有一定的参考价值。
目录标题
类型字节
char 1
short 2
int 16位电脑 2 32/64位 4
long 4 linux 8
long long 8
float 4
double 8
long double 8
c上机过程
(程序)编辑、(源程序,.c)编译、(目标程序,.obj)连接、(可执行程序,*.exe)运行、结果
转义字符
\\ddd 1-3位8进制 如:”\\101“是 101
\\xdd 2位 16进制 如:"\\x41" 是41
“>>” 左补最高符号位
“<<” 右补0
特殊程序
int d=(10,20);//d=20
extern void fun();//static 修饰只能再当前.c使用
标识符常量预编译
指针
在32位中指针4字节,64位中指针8字节
switch
c中 switch(a) a是整型,char
void *memset(void *str, int c, size_t n)
EOF对应的命令行是ctrl+z
预防头文件重复编译
数组
整型提升
char a=1,b=1,c;
c=a+b;
a,b先提升整形后(最高位是几补几),运算后再转化成char。即(char 和short使用前先转化成整型),这个过程叫做整型提升
sizeof()
返回的是无符号整型
指针
void类型的指针不可±操作
避免野指针
- 指针初始化
- 小心指针越界
- 指针指向空间释放即使置NULL
- 避免返回局部变量的地址
- 指针使用之前检查有效性
指针可以与数组之后的元素比较,不可以于之前的元素比较
int a[10]=1,2,3,4;
//a和&a[0]代表首元素地址,&a代表整个数组地址
二级指针
指针与函数
对于一个函数而言,当形参是指针变量时,对应的实参可以是指针变量,也可以是存储单元地址,此时的指针变量也遵守单向传递的原则(实参向形参传递)
指针函数
函数返回值是指针的函数。
函数指针
指向函数的指针变量
作用:通过函数指针数组,实现转移表的效果;实现函数回调效果
例子:
# include <stdio.h>
int Max(int, int);
int main(void)
int(*p)(int, int); //定义一个函数指针
int a, b, c;
printf("%d",Max); //或者 &Max 函数地址
p = Max; //把函数Max赋给指针变量p, 使p指向Max函数
printf("please enter a and b:");
scanf("%d%d", &a, &b);
c = (*p)(a, b);
//通过函数指针调用Max函数,可以化简为 p(a,b)
printf("a = %d\\nb = %d\\nmax = %d\\n", a, b, c);
return 0;
int Max(int x, int y)
int z;
if (x > y)
z = x;
else
z = y;
return z;
把0号地址对应的内存,当成一个函数指针来执行~
指针数组
存放指针的数组
int *a[3];
数组指针
指向数组的指针
数组与指针
- sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
- &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
- 除此之外所有的数组名都表示首元素的地址。
指针与字符串
char *a="123";
printf("%s",a);
回调函数
#include <stdio.h>
//qosrt函数的使用者得实现一个比较函数
int int_cmp(const void * p1, const void * p2)
return (*( int *)p1 - *(int *) p2);
int main()
int arr[] = 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 ;
int i = 0;
qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
printf( "%d ", arr[i]);
printf("\\n");
return 0;
使用回调函数,模拟实现qsort(采用冒泡的方式)。
#include<stdio.h>
int int_cmp(const void * p1, const void * p2)
return (*( int *)p1 - *(int *) p2);
void _swap(void *p1, void * p2, int size)
int i = 0;
for (i = 0; i< size; i++)
char tmp = *((char *)p1 + i);
*(( char *)p1 + i) = *((char *) p2 + i);
*(( char *)p2 + i) = tmp;
void bubble(void *base, int count , int size, int(*cmp )(const void *,const void *))
int i = 0;
int j = 0;
for (i = 0; i< count - 1; i++)
for (j = 0; j<count-i-1; j++)
if (cmp ((char *) base + j*size , (char *)base + (j + 1)*size) > 0)
_swap(( char *)base + j*size, (char *)base + (j + 1)*size, size);
int main()
int arr[] = 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 ;
//char *arr[] = "aaaa","dddd","cccc","bbbb";
int i = 0;
bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
printf( "%d ", arr[i]);
printf("\\n");
return 0;
指针面试题
strlen()函数形参时 * char类型的
以下是未定义应用(没有以/0结束),strlen是针对字符串使用的,
%p 打印地址
代码
// 第八题
char* c[] = "ENTER","NEW","POINT","FIRST" ;
char** cp[] = c + 3,c + 2,c + 1,c ;
char*** cpp = cp;
printf("%s\\n", **++cpp);
printf("%s\\n", *-- * ++cpp + 3);
printf("%s\\n", *cpp[-2] + 3);
printf("%s\\n", cpp[-1][-1] + 1);
// 第七题
//char* a[] = "work","at","alibaba" ;
//char** pa = a;
//pa++;
//printf("%s\\n", *pa);
// 第六题
//int aa[2][5] = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ;
//int* ptr1 = (int*)(&aa + 1);
//int* ptr2 = (int*)(*(aa + 1));
//printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
// 第五题
//int a[5][5];
//int(*p)[4];
这个赋值操作本身其实就是不科学的. (强行把两个不同类型的指针赋值, 就是有很大风险的)
//p = a;
//printf("%p,%d\\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
// 第四题
//int a[3][2] = (0, 1), (2, 3), (4, 5) ;
//int* p;
//p = a[0];
//printf("%d\\n", p[0]);
//printf("%d\\n", a[0][0]);
// 第三题
//int a[4] = 1, 2, 3, 4 ;
//int* ptr1 = (int*)(&a + 1);
//int* ptr2 = (int*)((int)a + 1);
//printf("%x,%x", ptr1[-1], *ptr2);
// 第二题
//printf("%p\\n", p + 0x1); // 0x100020 ?? 0x100014!!
//printf("%p\\n", (unsigned long)p + 0x1); // 0x100001
//printf("%p\\n", (unsigned int*)p + 0x1); // 0x100004
// 第一题
//int a[5] = 1, 2, 3, 4, 5 ;
//int* ptr = (int*)(&a + 1);
//printf("%d,%d", *(a + 1), *(ptr - 1)); // 2 5
//int a[3][4] = 0 ;
//printf("%d\\n", sizeof(a)); // 48
//printf("%d\\n", sizeof(a[0][0])); // 4 a[0][0] 结果是 int
//printf("%d\\n", sizeof(a[0])); // 16 a => int[3][4] a[0] => int[4]
//printf("%d\\n", sizeof(a[0] + 1)); // a => int[3][4] a[0] => int[4] a[0] + 1 => int[4] + 1 => int*
//printf("%d\\n", sizeof(*(a[0] + 1))); // a[0] => int[4] + 1 => int* * => int
//printf("%d\\n", sizeof(a + 1)); // 4 a => int[3][4] + 1 就是把数组转成指向首元素的指针. int(*)[4]
//printf("%d\\n", sizeof(*(a + 1))); // 16 a + 1 => int(*)[4] 再 * 结果就是 int[4]
//printf("%d\\n", sizeof(&a[0] + 1)); // a[0] => int[4] 再 & 得到了 int(*)[4] 再 + 1 还是 int(*)[4]
//printf("%d\\n", sizeof(*(&a[0] + 1))); // a[0] => int[4] 再 & 还是 int(*)[4] 再+1 还是 int(*)[4] 再来 * 得到 int[4]
//printf("%d\\n", sizeof(*a)); // a 是 int[3][4] *a 会导致 a 先隐式转成指向首元素的指针 int(*)[4] , 再 * 得到 int[4]
//printf("%d\\n", sizeof(a[3])); // 16 sizeof 是编译期求值, 于是这个 a[3] 不会在运行时执行
//char* p = "abcdef";
//printf("%d\\n", sizeof(p)); // 4 p 是 char* 类型
//printf("%d\\n", sizeof(p + 1)); // 4 p 是 char* 类型, p + 1 还是 char*
//printf("%d\\n", sizeof(*p)); // 1 p 是 char* *p 就是 char 类型
//printf("%d\\n", sizeof(p[0])); // 1 p[0] => *(p + 0) => *p
//printf("%d\\n", sizeof(&p)); // 4 p 是 char* &p => char**
//printf("%d\\n", sizeof(&p + 1)); // 4 同上
//printf("%d\\n", sizeof(&p[0] + 1)); // 4 p[0] 得到 char, & 得到 char* 再 + 1 还是 char*
//printf("%d\\n", strlen(p)); // 6 从 a 开始, 找 6 个字符就找到了 \\0
//printf("%d\\n", strlen(p + 1)); // 5 从 b 开始, 找 5 个字符就找到了 \\0
//printf("%d\\n", strlen(*p)); // 未定义行为. *p 得到的是 char 'a'. strlen 需要的是 char* 类型. 原则上讲是不应该编译通过的. 但是 C 语言对于类型检查不严格. strlen 就会尝试从 'a' ascii 这个地址, 开始去找 \\0. 由于这个地址是非法内存.
//printf("%d\\n", strlen(p[0])); // 未定义行为. 同上.
//printf("%d\\n", strlen(&p)); // 未定义行为. &p 得到的是 char**, 本身内部存的地址, 是 p 变量本身的地址. 这个地址和 \\0 无关, 尝试去找, 能不能找到这个 \\0 , 这是完全不好说的.
//printf("%d\\n", strlen(&p + 1)); // 未定义行为. 原因同上
//printf("%d\\n", strlen(&p[0] + 1)); // 5 p[0] 得到了 char 'a'. & 得到了 char*, 指向 'a', 再 + 1 得到 指向 'b' 的指针.
// " " 本身已经是字符串, 本身里面已经有 \\0 了.
// 得到的 arr 应该是一个 char[7]
//char arr[] = "abcdef";
//printf("%d\\n", sizeof(arr)); // 7
//printf("%d\\n", sizeof(arr + 0)); // 4 arr 是 char[7], +0 就相当于隐式转成了 char*
//printf("%d\\n", sizeof(*arr)); // 1 *arr 得到的是 'a' , char
//printf("%d\\n", sizeof(arr[1])); // 1 arr[1] 得到的是 'b', char
//printf("%d\\n", sizeof(&arr)); // 4 arr 是 char[7], &arr 是 char(*)[7] , 这也是指针类型.
//printf("%d\\n", sizeof(&arr + 1)); // 4 同上.
//printf("%d\\n", sizeof(&arr[0] + 1)); // 4 arr[0] 得到 char, 再 & 得到 char* , 再 +1 还是 char*
//printf("%d\\n", strlen(arr)); // 6 计算 strlen 的时候, 不算 \\0 本身.
//printf("%d\\n", strlen(arr + 0)); // 6 同上.
//printf("%d\\n", strlen(*arr)); // 未定义行为. *arr 得到的是 'a'. char, 不是 const char* , 尝试把 'a' 的 ascii 码当做内存地址了, 并尝试访问这个地址的内存.
//printf("%d\\n", strlen(arr[1])); // 未定义行为. arr[1] 得到的是 'b'. char, 不是 const char*. 尝试把 'b' 的 ascii 值当做一个内存地址了. 并尝试访问这个地址的内存.
//printf("%d\\n", strlen(&arr)); // 6 &arr 得到的是数组指针, char(*)[7], 和 形参要求的 const char* 不同的. 虽然类型不匹配, 但是结果碰巧是对.
//printf("%d\\n", strlen(&arr + 1)); // 未定义行为了.
//printf("%d\\n", strlen(&arr[0] + 1)); // 5 arr[0] 得到 字符 'a', 再 & 得到了 指向 'a' 的指针. 再 +1 得到了 指向 'b' 的指针. 从这个位置开始, 往后找 \\0,
//char arr[] = 'a','b','c','d','e','f' ;
//printf("%d\\n", sizeof(arr)); // 6 没有 \\0
//printf("%d\\n", sizeof(arr + 0)); // 4 arr 本来是 char[6], +0 就触发了隐式转换. 就变成了 char*
//printf("%d\\n", sizeof(*arr)); // 1 arr 是 char[6], 本来是个数组, 但是在 * 运算中也隐式转成了 char*, 针对 char* 解引用, 结果就是一个 char
//printf("%d\\n", sizeof(arr[1])); // 1 arr 是 char[6] 进行 [1] 操作, 就得到了一个 char 'b'
//printf("%d\\n", sizeof(&arr)); // 4 arr 是 char[6], &arr 得到了一个 char(*)[6]
//printf("%d\\n", sizeof(&arr + 1)); // 4 arr 是 char[6], &arr 得到了 char(*)[6], 再 +1 结果仍然是 char(*)[6]
//printf("%d\\n", sizeof(&arr[0] + 1)); // 4 arr[0] 得到了 char, 取地址, 得到了 char*, 再 + 1 仍然是 char*
// 代码中永远不能依赖未定义行为
//printf("%d\\n", strlen(arr)); // 未定义行为. arr 没有 \\0. 进行 strlen 就会出现数组下标越界.
//printf("%d\\n", strlen(arr + 0)); // 未定义行为. 和上面一样.
//printf("%d\\n", strlen(*arr)); // 未定义行为 arr 是 char[6], *arr 得到的是 char, 'a' 这个字符. 把 'a' 的 ascii 当做内存地址了. 这是一个非法地址, 尝试读取数据, 就会出现未定义行为.
//printf("%d\\n", strlen(arr[1])); // 同上. 这回访问的是 'b' 的 ascii 值对应的内存.
//printf("%d\\n", strlen(&arr)); // 未定义行为 &arr 得到的是数组指针. char(*)[6] , 虽然和形参类型不同, 但是大家都是指针, 里面存的地址相同. 这个时候也就相当于是从 'a' 地址开始往后找 \\0
//printf("%d\\n", strlen(&arr + 1)); // 未定义行为 &arr 得到了一个 char(*)[6]. 再 + 1 还是 char(*)[6] . 指针 +1, 地址要跳过一个元素. 这一跳就把整个数组的内容都跳过了.
//printf("%d\\n", strlen(&arr[0] + 1));
//int a[] = 1, 2, 3, 4 ;
//printf("%d\\n", sizeof(a)); // 16
//printf("%d\\n", sizeof(a + 0)); // 4 当前由于数组名参与了 + 0 运算, 隐式转成了指针.
//printf("%d\\n", sizeof(*a)); // 4 当前 a 参与了 * 运算, 也隐式转成了指针 int* . 针对 int* 解引用, 结果是一个 int
//printf("%d\\n", sizeof(a + 1)); // 4 和第二个代码一样, 数组名参与了 + 1 运算, 隐式转成了 int*
//printf("%d\\n", sizeof(a[1])); // 4 a 是数组, [1] 取到下标为 1 的元素, 是一个 int
//printf("%d\\n", sizeof(&a)); // 4 &a 得到一个数组指针. int(*)[4]
//printf("%d\\n", sizeof(*&a)); // 16 &a 得到一个数组指针. int(*)[4], 针对这个数组指针 * 操作, 结果就得到了一个 int[4]
//printf("%d\\n", sizeof(&*a)); // 4 a 是 int[4], *a, 其实就会先把 a 转成 int*, 然后 * 得到了一个 int. 然后再 & 就又得到了一个 int*
//printf("%d\\n", sizeof(&a + 1)); // 4 a 是 int[4], &a 是 int(*)[4], 再 + 1 仍然是 int(*)[4]
//printf("%d\\n", sizeof(&a[0])); // 4 a 是 int[4], a[0] 得到 int, 再 & 得到 int*
//printf("%d\\n", sizeof(&a[0] + 1)); // 4 a 是 int[4], a[0] 得到 int, 再 & 得到 int* , 再 + 1 结果仍然是 int*
//int arr[] = 9, 5, 2, 7, 3, 6 ;
//int len = sizeof(arr) / sizeof(arr[0]);
bubbleSort 的调用者, 需要手动指定一个比较函数, 交给 bubbleSort
这个比较函数起到的效果, 就是在定义排序的比较规则.
此时这个 less 函数并不是调用者自己来调用, 而是 由 bubbleSort 内部来进行调用.
以上是关于初始c笔记bing的主要内容,如果未能解决你的问题,请参考以下文章