文章目录
一、C库函数printf()
常用的几个:
格式字符 | 意义 |
---|---|
d | 以十进制形式输出带符号整数(正数不输出符号) |
u | 以十进制形式输出无符号整数 |
f | 以小数形式输出单、双精度实数 |
c | 输出单个字符 |
s | 输出字符串 |
p | 输出指针地址 |
lu | 32位无符号整数 |
llu | 64位无符号整数 |
二、C 语言中全局变量、局部变量、静态全局变量、静态局部变量的区别
从作用域看:
1、全局变量具有全局作用域。全局变量只需在一个源文件中定义,就可以作用于所有的源文件。当然,其他不包含全局变量定义的源文件需要用extern 关键字再次声明这个全局变量。
2、静态局部变量具有局部作用域,它只被初始化一次,自从第一次被初始化直到程序运行结束都一直存在(与局部变量的区别在于,局部变量存储在栈中,函数运行会产生内部的局部变量,函数结束时,局部变量会被释放;但是静态局部变量存储在内存中,只有程序都运行结束时,静态局部变量才会被释放),它和全局变量的区别在于全局变量对所有的函数都是可见的,而静态局部变量只对定义自己的函数体始终可见。
理解代码:
#include<stdio.h>
void fun()
{
static int a=1; a++;
printf("%d\n",a);
}
int main(void)
{
fun();
fun();
return 0;
}
输出:2,3
说明在第二次调用fun()函数时,没有再对变量a进行初始化为1的过程,而是直接从a=2开始自加运算。
3、局部变量也只有局部作用域,它是自动对象(auto),它在程序运行期间不是一直存在,而是只在函数执行期间存在,函数的一次调用执行结束后,变量被撤销,其所占用的内存也被收回。
4、静态全局变量也具有全局作用域,它与全局变量的区别在于如果程序包含多个文件的话,它作用于定义它的文件里,不能作用到其它文件里,即被static关键字修饰过的变量具有文件作用域。这样即使两个不同的源文件都定义了相同名字的静态全局变量,它们也是不同的变量。
从分配内存空间看:
1、全局变量,静态局部变量,静态全局变量都在静态存储区分配空间,而局部变量在栈里分配空间
2、全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
1)静态变量会被放在程序的静态数据存储区(全局可见)中,这样可以在下一次调用的时候还可以保持原来的赋值。这一点是它与堆栈变量和堆变量的区别。
2)变量用static告知编译器,自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。
从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。因此static 这个说明符在不同的地方所起的作用是不同的。应予以注意。
三、形参与实参
- 在定义函数中制定的形参,在没有出现函数调用时不占用内存中的存储单元。在函数调用时才分配内存;
- 将实参的值传递给形参;
- 在执行函数时,由于形参已经有值。可以用形参进行运算;
- 通过return语句将函数值返回,若无返回值,则无return;
- 调用结束后,形参被释放掉,实参保留原值(单向传值)。
可以对比函数参数传递常用的三种方式:值传递、指针传递、引用传递
参考https://blog.csdn.net/weixin_44309097/article/details/124086049
四、数组
1.数组名
在 C 语言中,数组名表示数组的地址,即数组首元素的地址。当我们在声明和定义一个数组时,该数组名就代表着该数组的地址。
int myArray[5] = {10, 20, 30, 40, 50};
在这里,myArray 是数组名,也代表着数组的地址,即第一个元素的地址。
**数组名与指针:**数组名也被认为是一个常量指针。我们可以使用&运算符来获取数组的地址,如下所示:
int myArray[5] = {10, 20, 30, 40, 50};
int *ptr = &myArray[0]; // 或者直接写作 int *ptr = myArray;
prinf("%d",*ptr);//输出值为10;
上述代码中ptr被被赋值为数组myArray的地址,*ptr代表指向myArray数组第一个值的指针,也就是10。
2.指针与数组
- 看下列代码能更清晰地理解指针和数组的区别:
#include <stdio.h>
int main()
{
int a[2] = {1,2};
printf("a = %d\n",a[0]);
printf("*(a+0) = %d\n",*(a + 0));
printf("a[1] = %d\n",a[1]);
printf("*a = %d\n",*a);
printf("*(a+1) = %d\n",*(a + 1));
printf("\n");
printf("a 的地址:%p\n",a);
printf("(a+0)的地址:%p\n",(a + 0));
printf("(a+1)的地址:%p\n",(a + 1));
// %p 读入一个指针
printf("\n");
return 0;
}
输出结果为:
a = 1
*(a+0) = 1
a[1] = 2
*a = 1
*(a+1) = 2
a 的地址:0x7ffe9e227634
(a+0)的地址:0x7ffe9e227634
(a+1)的地址:0x7ffe9e227638
因此a[0],a[1]
其实是一个指针,等同于*a,*(a+1)
- 传递数组给函数
方式1:
void myFunction(int *param){}//*param=param[]
方式2:
void myFunction(int param[10]){}
方式3:
void myFunction(int param[]){}
3.注意:数组用作函数入参时,最好带上其长度。
错误的做法(我认为可以理解为形参和实参的传递):
void getSize(int a[]){
int i;
i=sizeof(a);//这里的i在linux中是4,在Windows中也是4。是int *的大小,不管你传入的a有几个元素
int b;
b=sizeof(a)/sizeof(a[0]);//逻辑没错,a的大小除以元素的大小得到长度,可是sizeof(a)并不是数组占的空间,而是指针,也就是i
}
int main(){
int a[]={1,2,3,4,5};
getSize(a);
}
正确的做法:
void getSize(int a[],int size){
//int i;
//i=sizeof(a);//这里的i在linux中是4,在Windows中也是4,是int *的大小,不管你传入的a有几个元素
//int b;
//b=sizeof(a)/sizeof(a[0]);//逻辑没错,a的大小除以元素的大小得到长度,可是sizeof(a)并不是数组占的空间,而是指针,也就是i
int k = size;//已经保存入参大小
//遍历或者其他操作
…………
}
int main(){
int a[]={1,2,3,4,5};
int size = sizeof(a)/sizeof(a[0]);//此时a并不是入参,sizeof可以得到开发人员想要的结果
getSize(a,size);
}
- 从函数中返回一个数组
如果想要从函数返回一个一维数组,您必须声明一个返回指针的函数,如下:
int * myFunction(){}
下面的函数会生成 10 个随机数,并使用数组来返回它们,具体如下:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/* 要生成和返回随机数的函数 */
int * getRandom( )
{
static int r[10];
int i;
/* 设置种子 */
srand( (unsigned)time( NULL ) );
for ( i = 0; i < 10; ++i)
{
r[i] = rand();
printf( "r[%d] = %d\n", i, r[i]);
}
return r;//返回指向数组第一个值的指针的地址(即返回了一个指针)
}
/* 要调用上面定义函数的主函数 */
int main ()
{
/* 一个指向整数的指针 */
int *p;
int i;
p = getRandom();//将函数返回的指针地址赋给p,则*p的值就是数组的第一个值
for ( i = 0; i < 10; i++ )
{
printf( "*(p + %d) : %d\n", i, *(p + i));
}
return 0;
}
- 静态数组和动态数组
静态数组的声明和初始化:
int staticArray[5]; // 静态数组声明
int staticArray[] = {1, 2, 3, 4, 5}; // 静态数组声明并初始化
int length=sizeof(staticArray)/sizeof(staticArray[0]);//获取静态数组长度
动态数组的声明、内存分配和释放实例:
int size = 5;
int *dynamicArray = (int *)malloc(size * sizeof(int)); // 动态数组内存分配
// 使用动态数组
free(dynamicArray); // 动态数组内存释放
以上为24.7.6总结
五、指针
1、指针的定义
指针也就是内存地址,指针变量是用来存放内存地址的变量。
int *ip; /* 一个整型的指针 */
double *dp; /* 一个 double 型的指针 */
float *fp; /* 一个浮点型的指针 */
char *ch; /* 一个字符型的指针 */
所有实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,对应指针的值的类型都是一样的,都是一个代表内存地址的长的十六进制数。
2、指针的简单使用
#include <stdio.h>
int main ()
{
int var = 20; /* 实际变量的声明 */
int *ip; /* 指针变量的声明 */
ip = &var; /* 在指针变量中存储 var 的地址 */
printf("var 变量的地址: %p\n", &var );
/* 在指针变量中存储的地址 */
printf("ip 变量存储的地址: %p\n", ip );
/* 使用指针访问值 */
printf("*ip 变量的值: %d\n", *ip );
return 0;
}
输出:
var 变量的地址: 0x7ffeeef168d8
ip 变量存储的地址: 0x7ffeeef168d8
*ip 变量的值: 20
3、指针的算术运算
可以对指针进行四种算术运算:++、–、+、-。
int *ptr; //假设ptr是一个指向地址为1000的整形指针;
printf("%p",ptr++);//则此时*ptr会指向地址为1004这个内存地址,则输出值为1004。
//ptr 每增加一次,它都将指向下一个整数位置,即当前位置往后移 4 字节。
char *p;//如果 ptr 指向一个地址为 1000 的字符,上面的运算会导致指针指向位置 1001,因为下一个字符位置是在 1001。
printf("%p",p++);//输出1001
4、指针的比较
在 C 语言中,可以比较指针来确定它们的关系。指针比较主要用于确定两个指针是否指向相同的内存位置或确定一个指针是否位于另一个指针之前或之后。
指针可以用关系运算符进行比较,如==、!=、<、>、<= 和 >=。如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。
比如:指针大小比较:
#include <stdio.h>
int main() {
int arr[] = {10, 20, 30, 40, 50};
int *ptr1 = &arr[1]; // 指向 arr[1],值为 20
int *ptr2 = &arr[3]; // 指向 arr[3],值为 40
if (ptr1 < ptr2) {
printf("ptr1 在 ptr2 之前\n"); // 这行会被输出
} else {
printf("ptr1 在 ptr2 之后或相同位置\n");
}
if (ptr1 > ptr2) {
printf("ptr1 在 ptr2 之后\n");
} else {
printf("ptr1 在 ptr2 之前或相同位置\n"); // 这行会被输出
}
return 0;
}
输出:
ptr1 在 ptr2 之前
ptr1 在 ptr2 之前或相同位置
总结:
- 相等比较 (== 和 !=): 用于判断两个指针是否指向相同的内存位置。
- 大小比较 (<, >, <=, >=): 通常用于指针遍历数组或内存块时,判断一个指针是否在另一个指针之前或之后。
- 需要注意的是,指针比较只有在指向同一个数组或同一内存块时才有意义,否则行为未定义。
5、指向指针的指针
#include <stdio.h>
int main ()
{
int V;
int *Pt1;
int **Pt2;
V = 100;
/* 获取 V 的地址 */
Pt1 = &V;
/* 使用运算符 & 获取 Pt1 的地址 */
Pt2 = &Pt1;
/* 使用 pptr 获取值 */
printf("var = %d\n", V );
printf("Pt1 = %p\n", Pt1 );
printf("*Pt1 = %d\n", *Pt1 );
printf("Pt2 = %p\n", Pt2 );
printf("**Pt2 = %d\n", **Pt2);//注意输出的值为100
return 0;
}
输出:
var = 100
Pt1 = 0x7ffee2d5e8d8
*Pt1 = 100
Pt2 = 0x7ffee2d5e8d0
**Pt2 = 100