C语言学习笔记
- 一、变量、函数
-
- 1. typedef
- 2. size_t
- 3. enum(枚举)
- 二、指针
-
- 1. 指针基本概念:*&
- 2. 函数指针与回调函数
-
- 2.1 函数指针作为某个函数的参数
- 2.2 回调函数·传值调用函数
- 2.3 回调函数·引用调用函数
- 三、补充
-
- 1. 字符串
- 2. 结构体
-
- 指向结构的指针
- 3. 共用体
- 4. 位域
- 四、C语言实例
一、变量、函数
1. typedef
使用typedef为现有类型创建别名,定义易于记忆的类型名
typedef int size;
void measure(size*psz);
typedef 还可以掩饰复合类型,如指针和数组。
例如,你不用像下面这样重复定义有 81 个字符元素的数组:
char line[81];
char text[81];
只需这样定义,Line类型即代表了具有81个元素的字符数组,使用方法如下:
typedef char Line[81];
Line text,line;
同样,可以像下面这样隐藏指针语法:
typedef char* pstr;
int mystrcmp(const pstr p1,const pstr p3);
const char*
和
char* const
表达的并非同一意思,
const char*
的意思是创建一个指向char类型的指针且不能更改指向地址上的值,而
char* const
则是不能更改指向的地址;
char* const p
: 定义一个指向字符的指针常数,即const指针,常量指针。
const char* p
:定义一个指向字符型常量的指针。
2. size_t
理解:无符号整型,作为大小的表示量。int可正可负,只作为整数。
size_t 的定义在 stddef.h、stdio.h、stdlib.h、string.h、time.h 和 wchar.h 这些标准 C 的头文件中,包含以上任一头文件,则表明 size_t 将作为一个全局关键字。
在 32 位架构中被普遍定义为:
在 64 位架构中被定义为:
从定义可以看出,size_t 是一种无符号的整型(unsigned int、unsigned long、unsigned long long),取值范围是目标平台下最大的可能范围。
3. enum(枚举)
枚举是 C 语言中的一种基本数据类型,它可以让数据更简洁,更易读。
枚举语法定义格式为:
菜鸟教程展开>>
二、指针
1. 指针基本概念:*&
笔记:一个指针指向一个数组,用 ptr = &var[0] 与 ptr = var一样,默认存储该数组首位地址。
#include <stdio.h>
const int MAX = 3;
int main()
{
int var[] = { 10, 100, 200 };
int i, * ptr,*pt;
/* 指针中最后一个元素的地址 */
ptr = &var[MAX - 1];
pt = var;
for (i = 0; i < 2 ; i++)
{
pt = pt++;
}
for (i = MAX; i > 0; i--)
{
printf("存储地址:var[%d] = %p\n", i - 1, ptr);
printf("存储值:var[%d] = %d\n", i - 1, *ptr);
printf("存储地址:var[%d] = %p\n", i - 1, pt);
/* 指向下一个位置 */
ptr--;
pt--;
}
return 0;
}
2. 函数指针与回调函数
2.1 函数指针作为某个函数的参数
函数指针变量可以作为某个函数的参数来使用的,回调函数就是一个通过函数指针调用的函数。
简单讲:回调函数是由别人的函数执行时调用你实现的函数。
以下是来自知乎作者常溪玲的解说:
你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。
- 实例
实例中
populate_array
函数定义了三个参数,其中第三个参数是函数的指针,通过该函数来设置数组的值。
实例中我们定义了回调函数
getNextRandomValue
,它返回一个随机值,它作为一个函数指针传递给
populate_array
函数。
populate_array 将调用 10 次回调函数,并将回调函数的返回值赋值给数组。
#include <stdlib.h>
#include <stdio.h>
// 定义回调函数
void populate_array(int *array, size_t arraySize, int (*getNextValue)(void))
{
for (size_t i=0; i<arraySize; i++)
array[i] = getNextValue();
}
// 获取随机值
int getNextRandomValue(void)
{
return rand();
}
int main(void)
{
int myarray[10];
/* getNextRandomValue 不能加括号,否则无法编译,因为加上括号之后相当于传入此参数时传入了 int , 而不是函数指针*/
populate_array(myarray, 10, getNextRandomValue);
for(int i = 0; i < 10; i++) {
printf("%d ", myarray[i]);
}
printf("\n");
return 0;
}
代码解释:
int *array
与
int array[]
效果相同,但是两者区别在于:
其实使用数组名定义也相当于指针
int a[]
.比如,要访问
int *a
实际上是先获得这个数组的头指针
int a[5]
,然后在内存中偏移5个int的数据长度
int*a
,也就是从代码的理解上,可以认为
int *(a+5)
与
int a[5]
int *(a+5)
等价,
作者:林扬飞 链接:https://www.zhihu.com/question/23278865/answer/28430556
2.2 回调函数·传值调用函数
向函数传递参数的传值调用方法,把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数不会影响实际参数。
默认情况下,C 语言使用传值调用方法来传递参数。一般来说,这意味着函数内的代码不会改变用于调用函数的实际参数。
/* 函数定义 */
void swap(int x, int y)
{
int temp;
temp = x; /* 保存 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 temp 赋值给 y */
return;
}
菜鸟教程展开>>
补充:C语言return的用法详解
2.3 回调函数·引用调用函数
通过引用传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。
传递指针可以让多个函数访问指针所引用的对象,而不用把对象声明为全局可访问。
/* 函数定义 */
void swap(int *x, int *y)
{
int temp;
temp = *x; /* 保存地址 x 的值 */
*x = *y; /* 把 y 赋值给 x */
*y = temp; /* 把 temp 赋值给 y */
return;
}
菜鸟教程展开>>
三、补充
1. 字符串
C 中有大量操作字符串的函数:
序号 | 函数 | 目的 |
---|---|---|
1 | strcpy(s1,s2); | 复制字符串 s2 到字符串 s1。 |
2 | strcat(s1,s2); | 连接字符串 s2 到字符串 s1 的末尾。 |
3 | strlen(s1,s2); | 返回字符串 s1 的长度。 |
4 | strcmp(s1,s2); | 如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回小于 0;如果 s1>s2 则返回大于 0。 |
5 | strchr(s1,s2); | 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。 |
6 | strstr(s1,s2); | 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。 |
2. 结构体
struct tag {
member-list
member-list
member-list
...
} variable-list ;
tag
是结构体标签。
member-list
是标准的变量定义,比如 int i; 或者 float f,或者其他有效的变量定义。
variable-list
结构变量,定义在结构的末尾,最后一个分号之前,您可以指定一个或多个结构变量。下面是声明 Book 结构的方式:
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} book;
指向结构的指针
您可以定义指向结构的指针,方式与定义指向其他类型变量的指针相似,如下所示:
现在,您可以在上述定义的指针变量中存储结构变量的地址。为了查找结构变量的地址,请把 & 运算符放在结构名称的前面,如下所示:
为了使用指向该结构的指针访问结构的成员,您必须使用
->
运算符,如下所示:
笔记:
struct A *p;
与
struct A {};
区别
//定义A为:内含整数的结构体//
struct A {
int data;
};
int main()
{
struct A *b; //定义b为指向 满足标签A的结构体 的指针//
struct A c; //定义c为满足标签A的结构体//
b = &c; //指针b中存的是c的地址//
c.data = 100;
printf("%d\n", b->data);
return 0;
}
菜鸟教程展开>>
3. 共用体
展开>>补充:共用体和结构体的区别
4. 位域
#include <stdio.h>
#include <string.h>
/* 定义简单的结构 */
struct
{
unsigned int widthValidated;
unsigned int heightValidated;
} status1;
/* 定义位域结构 */
struct
{
unsigned int widthValidated : 1;
unsigned int heightValidated : 1;
} status2;
int main( )
{
printf( "Memory size occupied by status1 : %d\n", sizeof(status1));
printf( "Memory size occupied by status2 : %d\n", sizeof(status2));
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
Memory size occupied by status1 : 8
Memory size occupied by status2 : 4
笔记:位数由0到32,增加4个字节。位数超过32,增加4个字节。即占用33位,消耗内存为8字节。
菜鸟教程展开>>