C语言基础知识一
一.二维数组
连续存储,即内存地址是连续的。
#include <stdio.h>
int main(int argc, char *argv[])
{
int a[2][2];
int i, j;
a[0][0] = 1;
a[0][1] = 2;
a[1][0] = 3;
a[1][1] = 4;
//第二种赋值方式,定义加初始化
int b[2][2] = {1,2,3,4};
int c[2][2] = {{1,2},{3,4}};
printf("size:%d\n", sizeof(a));
// sizeof(a):代表整个二维数组的内存大小
// sizeof(a[0]):代表二维数组的一行的内存大小
// sizeof(a)/sizeof(a[0]):就是代表数组总共有几行
printf("row: %d\n", sizeof(a)/sizeof(a[0]));
// sizeof(a[0][0])代表二维数组中一个元素的内存大小
// sizeof(a[0])/sizeof(a[0][0])表示数组总共有几列
printf("col: %d\n", sizeof(a[0])/sizeof(a[0][0]));
// 遍历数组
for (i = 0; i < sizeof(a)/sizeof(a[0]); i++) {
for (j = 0; j < sizeof(a[0])/sizeof(a[0][0]); j++) {
printf("a[%d][%d]:%d [%p]\n",i ,j ,a[i][j], &a[i][j]);
}
}
return 0;
}
二.void类型
*1.void 表示任何类型的指针都可以直接赋值给它,无需进行强制类型转换,反之并不成立。
void *p1;
int *p2;
//正确,
p1 = p2;
//错误,
p2 = p1;
2.void指针
不能对void指针进行算法操作
void *pvoid;
pvoid++;
pvoid += 1;
//主要是进行算法操作的指针必须是确定知道其只想数据类型大小的。
int *pint;
pint++; //正确
void *pvoid;
(char *)pvoid++; //ANSI:正确 GNU: 正确
(char *)pvoid += 1; // ANSI:错误,GNU:正确
3.const修饰的只读变量
编译器通常不为普通const只读变量分配存储空间,而是将它们保存在符号表中,这使得他成为一个编译期间的值,没有了存储与读内存的操作,使得他的效率也很高的。
如:
#define M 3 //宏常量
const int N = 5; //此时并未将N放入内存
int i = N; //此时为N分配内存,以后不再分配
int I = M; //预编译期间进行宏替换,分配内存
int j = N; //没有内存分配
int J = M; //再进行宏替换,又一次分配内存
修饰指针:
//记忆的诀窍:先忽略类型名,我们看const离哪个近,就是修饰谁。
const int *p; //p可变,p指向的对象不可变
int const *p; //p可变,p指向的对象不可变
int * const p; //p不可变,p指向的对象可变
const int * const p; //指针p和p指向的对象都不可变
修饰函数的返回值:
const修饰符也可以修饰返回值,返回值不可被改变
const int Fun(void);
在另一个连接文件中引用const只读变量
extern const int i; //正确的声明
extern const int j = 10;//错误,只读变量的值不能改变
三.空结构体有多大
#include <stdio.h>
int main(int argc, char *argv[])
{
struct student {
}stu;
//看具体的编译而言的,在vc++6.0上显示为1byte
//在gcc下编译为0byte
printf("sizeof(stu) = %d\n", sizeof(stu));
return 0;
}
四.union类型数据
#include<stdio.h>
union {
int i;
char a[5];
} *p,u;
int main()
{
p = &u;
p->a[0] = 0x39;
p->a[1] = 0x38;
p->a[2] = 0x37;
// 由于union字节对齐原因,大小为8
printf("sizeof(u) %d\n", sizeof(u));
//显示结果是小端模式:即高存高,低存低
// 0x373839
printf("i = %#x\n", p->i);
return 0;
}
五.大端模式和小端模式
#include<stdio.h>
int checkBigEndianOrLittleEndian() {
union {
int i;
char a;
} ch;
ch.i = 1;
return (ch.a == 1);
}
int main() {
printf("check system : %d\n", checkBigEndianOrLittleEndian());
}
六.typedef
typedef struct student {
char name[32];
int age;
} Stu_st, *Stu_pst;
// 其实就是整体上struct student取个别名为Stu_st,将struct student *取个别名Stu_pst
所以
1.struct student stu1 等价于 Stu_st stu1
2.struct student *stu2 等价于 Stu_pst stu2 等价于 Stu_st *stu2
如果将typedef与const放在一起来看看:
const Stu_pst stu3;
Stu_pst const stu4;
//大多数人认为第一个中const修饰的是stu3指向的对象,
//第二个中const修饰的是stu4这个指针。
//其实编译器来说只认为Stu_pst是一个类型名,所以
//上面的两个均是表示为修饰的是stu3,stu4这个指针。
#include<stdio.h>
typedef struct student {
char name[32];
int age;
}Stu_st, *Stu_pst;
int main() {
const Stu_pst stu3 = {"tom", 32};
Stu_pst const stu4 = {"danny", 18};
//下面两条语句可以编译通过就证明
//修饰的是stu3,stu4这个指针,并非stu3,stu4指向的对象。
stu3->age = 22;
stu4->age = 23;
//当指针发生改变时,编译就会报错的
stu4 = stu3;
return 0;
}
七.预处理