苏嵌实训——day5
一、指针和二维数组
#include <stdio.h>
int main(int argc, const char *argv[])
{
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
//&a:二维数组名取地址表示地址的升级,将行指针变化为指向整个数组的指针
printf("&a = %p\n",&a);
printf("&a = %p\n",&a + 1);
printf("a = %p\n",a);
printf("a = %p\n",a + 1);
//*a:二维数组名取*表示地址降级,
//将行指针降级为列指针,此时*a的操作空间是4个字节
printf("*a = %p\n",*a);
printf("*a = %p\n",*a + 1);
printf("**a = %d\n",**a);
printf("*(*a+1) = %d\n",*(*a+1));
//a[i][j]------> *(*(a + i) + j)
int i,j;
for(i = 0 ; i < 3;i++)
{
for(j = 0 ; j < 4;j++)
{
//printf("%-5d",a[i][j]);
printf("%-5d",*(*(a+i) + j));
}
putchar(10);
}
return 0;
}
二、数组指针
#include <stdio.h>
int main(int argc, const char *argv[])
{
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
//一级指针p的操作空间只有4个字节,但是二维数组的数组名操作空间是一行数据的大小,也就是16个字节,所以无法
//使用一级指针保存二维数组的首地址
//int *p = a; //错误
//二级指针用于保存一级指针的地址,二维数组名是一个行指针,性质和操作空间不一致
// int **p = a; //错误
//
//真正用于保存二维数组的数组名的地址或者说定义一个行指针的方法是定义一个数组指针
//数组指针:本质是一个指针,指针指向一个二维数组,就是定义一个行指针
//格式: 数据类型 (*变量名)[列数]
//用途: 数组指针的真正用途试讲一个二维数组通过传参传递给指定函数
int (*p)[4] = a;
int i,j;
for(i = 0 ; i < 3;i++)
{
for(j = 0 ; j < 4;j++)
{
printf("%-5d",p[i][j]);
}
putchar(10);
}
return 0;
}
三、指针数组
指针数组:本质是一个数组,数组的每一个元素是一个指针
格式: 数据类型 *变量名[数组下标]
例如: int *a[4];
解释:定义一个名为a的指针数组,数组一共有4个元素,每个元素都是int *的指针
#include <stdio.h>
int main(int argc, const char *argv[])
{
int *a[4];
int num1 = 100;
int num2 = 200;
int num3 = 300;
int num4 = 400;
//使用指针数组保存变量的地址
//指针数组的每一个元素都是int *的指针变量
a[0] = &num1;
a[1] = &num2;
a[2] = &num3;
a[3] = &num4;
//遍历指针数组
int i;
for(i = 0 ; i < 4;i++)
{
printf("%d ",*a[i]);
}
putchar(10);
char *p[4];
char s1[] = "hello world";
char s2[] = "hello beijing";
char s3[] = "hahahah";
char s4[] = "nihao nanjing";
p[0] = s1;
p[1] = s2;
p[2] = s3;
p[3] = s4;
for(i = 0 ; i < 4;i++)
{
printf("p[%d] =%s\n",i,p[i]);
}
return 0;
}
四、指针和字符串
#include <stdio.h>
int main(int argc, const char *argv[])
{
char s1[] = "hello world";
char s2[] = "hello world";
printf("s1 = %p\ns2 = %p\n",s1,s2);
char *p1 = "hello world";
char *p2 = "hello world";
printf("p1 = %p\np1 = %p\n",p1,p2);
//p1[5] = '8';
//printf("p1 = %s\n",p1);
p1 = "www.baidu.com";
printf("p1 = %p %s\n",p1,p1);
return 0;
}
五、多级指针
#include <stdio.h>
int main(int argc, const char *argv[])
{
int a = 100;
//一级指针保存普通变量的地址
int *p = &a;
//二级指针保存一级指针的地址
int **q = &p;
//三级指针保存二级指针的地址
int ***w = &q;
printf("a = %d %d %d %d\n",a,*p,**q,***w);
printf("a = %p %p %p %p\n",&a,p,*q,**w);
printf("p = %p %p %p\n",&p,q,*w);
printf("q = %p %p\n",&q,w);
return 0;
}
六、const 关键词
6.1 全局变量和局部变量
#include <stdio.h>
//我们内存分为很多区域,
//定义变量时会根据定义的位置和存储类型放在特定的区域里
//简单的说:全局区,静态区,栈区,堆区,常量区,代码区等
//
//在函数外部定义的变量称之为全局变量,保存在内存的全局区
//全局变量可以在当前代码的任意位置(主函数或者子函数)使用
int num = 100;
//全局变量如果不初始化,会自动初始化为0
int a;
int myval = 999;
int main(int argc, const char *argv[])
{
printf("num = %d\n",num);
num = 888;
printf("num = %d\n",num);
printf("a = %d\n",a);
//在函数内部定义的变量称之为局部变量,如果不使用存储类型修饰,则当前变量存储在栈区
//栈区的空间如果不初始化,里面存放的是随机值
int b;
printf("b = %d\n",b);
printf("myval = %d\n",myval);
int myval = 888;
printf("myval = %d\n",myval);
return 0;
}
6.2 const 修饰全局变量和局部变量
const关键字主要表示只读,但是也要分场合
#include <stdio.h>
//const修饰全局变量,只能使用这个变量的值,无论如何也不能改变
const int num = 100;
int main(int argc, const char *argv[])
{
const int myval = 111;
//int *p = #
//*p = 111;
//printf("*p = %d\n",num);
//myval = 222;
//printf("myval = %d\n",myval);
//const如果修饰的一个局部变量,不能直接通过变量改值,但是可以通过变量保存地址的方法修改值。
int *q = &myval;
*q = 222;
printf("myval = %d\n",myval);
printf("myval = %p q = %p\n",&myval,q);
return 0;
}
6.3 const 修饰指针变量和修饰指针变量的类型
#include <stdio.h>
int main(int argc, const char *argv[])
{
int a = 100;
//int *p = &a;
//const修饰的是指针变量的类型,无法通过指针变量修改指向的地址的内容
//const int *p = &a;
//const修饰的是 指针变量p,
//无法修改指针的指向
int *const p = &a;
//const修饰的是指针变量的类型,无法通过指针变量修改指向的地址的内容
//int const *p = &a;
//const既修饰指针变量的类型又修饰指针变量,
//则p的指向和p指向的内存的值都无法修改
//const int * const p = &a;
//*p = 300;
int b = 400;
p = &b;
return 0;
}
七、存储类型
auto
register
static
extern
7.1 auto
默认定义的变量的时候,如果不加存储类型,一般就表示一个局部变量
例如:
auto int num = 100;
int a = 100;
7.2 register
register修饰的变量可能会在寄存器空间开辟区域,这样运行效率会大大提高
寄存器的空间不一定能申请到,申请不到,会自动变成auto修饰
#include <stdio.h>
int main(int argc, const char *argv[])
{
register int i,j;
for(i = 1;i <=10000;i++)
{
for(j = 1;j <= 10000;j++)
{
}
}
return 0;
}
7.3 static
#include <stdio.h>
int main(int argc, const char *argv[])
{
//static修饰的变量保存在静态区
//如果不初始化会自动赋值为0
static int num;
printf("num = %d\n",num);
return 0;
}
7.4 extern
extern外部引用,用extern修饰的变量可以在其它文件中使用,在一个文件中定义的全局变量可以在当前文件中使用extern获取到指定的变量,对应变量的值是可以使用的
八 函数
8.1 函数的相关概念
函数就是将一堆要执行的代码放在一个代码块例,然后给这段代码起个名字,,只要使用名字就相当于执行里面的代码块,所以函数很好的解决了代码的重用性问题。
1.通过函数名找到函数的入口地址(函数名就是地址)
2.给形参分配空间
3.传参,传值(把实参的值传递给形参的值)(值传递,地址传递)
4.执行函数体
5.结果返回到调用的地方
6.释放空间(栈空间)
8.2 定义一个函数以及函数的调用
8.2.1 函数的定义和调用
#include <stdio.h>
//一般函数定义在main函数的外部,方便其他程序调用
//myfun:函数名,是一个标识符,满足标识符的命名规则
//void:返回值类型,如果没有返回值,写void
//():里面是要传入的参数,如果没有参数,可以不写,但是()必须有
void myfun()
{
printf("----------------\n");
printf("--helloworld----\n");
printf("-----nihao beijing----\n");
printf("--------nihao nanjing--------\n");
return;
}
int main(int argc, const char *argv[])
{
//调用一个没有参数和返回值的函数
//调用语法:函数名(参数)
//注意:调用时一定不能加返回值类型
//如果没有参数可以不传,有参数必须传
//没有参数,函数名()必须要加
myfun();
myfun();
myfun();
myfun();
myfun();
myfun();
myfun();
return 0;
}
8.2.2 函数的声明
#include <stdio.h>
//函数的声明
void myfun();
int main(int argc, const char *argv[])
{
myfun();
myfun();
myfun();
return 0;
}
void myfun()
{
printf("----------------\n");
printf("--helloworld----\n");
printf("-----nihao beijing----\n");
printf("--------nihao nanjing--------\n");
return;
}
8.2.3 函数立案调用其它函数
#include <stdio.h>
//函数的声明
void myfun();
void myfun1();
void myfun2();
void myfun3();
void myfun4();
int main(int argc, const char *argv[])
{
myfun4();
return 0;
}
void myfun()
{
printf("----------------\n");
printf("--helloworld----\n");
printf("-----nihao beijing----\n");
printf("--------nihao nanjing--------\n");
return;
}
void myfun1()
{
printf("11111111\n");
myfun();
return;
}
void myfun2()
{
printf("22222222\n");
myfun1();
return;
}
void myfun3()
{
printf("3333333333333\n");
myfun2();
return;
}
void myfun4()
{
myfun3();
return;
}
练习: 实现mystrcmp函数的功能
#include <stdio.h>
int Mystrcmp(const char *p,const char *q)
{
while(*p != '\0' && *q != '\0')
{
if(*p > *q)
{
return 1;
}
else if(*p < *q)
{
return -1;
}
p++;
q++;
}
if(*p == '\0' && *q != '\0')
{
return -1;
}
else if(*p != '\0' && *q == '\0')
{
return 1;
}
else if(*p == '\0' && *q == '\0')
{
return 0;
}
}
int main(int argc, const char *argv[])
{
char s1[] = "hellworld";
char s2[] = "hello z";
int ret = Mystrcmp(s1,s2);
if(ret == 0)
{
printf("s1 = s2\n");
}
else if(ret > 0)
{
printf("s1 > s2\n");
}
else
{
printf("s1 < s2\n");
}
return 0;
}
8.3 函数的参数
#include <stdio.h>
int i = 100;
int j = 200;
void myadd1();
void myadd2();
void myadd3(int a,int b);
int main(int argc, const char *argv[])
{
myadd1();
myadd2();
myadd3(1,2);
return 0;
}
void myadd1()
{
int sum = i+j;
printf("sum = %d\n",sum);
return;
//return 1;
}
void myadd2()
{
i = 300;
j = 400;
return;
}
void myadd3(int a,int b) //形参,参数之间用逗号隔开
{
int sum = a + b;
printf("sum = %d\n",a+b);
}
8.4 函数的返回值
#include <stdio.h>
int add(int a,int b);
int main(int argc, const char *argv[])
{
int a = 1,b = 2; //实参
int ret = add(a,b);
return 0;
}
//函数名左边的是返回值类型,要与返回值的类型一致
int add(int a,int b)
{
int sum = a+b;
//函数的返回值:含税执行结束的时候,如果需要将有些值返回
//给其他函数,那么就需要有返回值,反之不需要返回值
return sum; //将结果返回
}
练习:
编写一个函数,返回a的n次幂
#include <stdio.h>
int myPow(int a,int n);
int main(int argc, const char *argv[])
{
printf("%d\n",myPow(3,5));
return 0;
}
int myPow(int a,int n)
{
if(a == 0)
{
return 0;
}
int num = 1;
while(n != 0)
{
num = num *a;
n--;
}
return num;
}
8.5 函数的传参方式
8.5.1 传参方式
一般分为三种:
全局变量传参:
一般不用,因为全局变量本身就可以在函数内部直接使用,不需要专门传参
值传参:将实参的值传递给形参
地址传参:将实参的地址传给形参
#include <stdio.h>
void Swap(int *a,int *b);
int main(int argc, const char *argv[])
{
int a = 100,b = 200;
printf("a = %d,b = %d\n",a,b);
Swap(&a,&b);
printf("a = %d,b = %d\n",a,b);
return 0;
}
void Swap(int *a,int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}
8.5.2 一维数组传参
#include <stdio.h>
//一维数组作为参数传给函数,有两种方法
//第一种:传int a[]
//第二种:传 int *a //常用
//第三种:传 int a[100]
//void myOrder(int *arr,int n)
//void myOrder(int arr[],int n)
void myOrder(int arr[1000],int n)
{
int i;
printf("sizeof(arr) = %ld\n",sizeof(arr));//arr只是一个指针变量,不是数组名,所以无法通过这个指针变量获取数组所占空间大小
for(i = 0 ; i < n;i++)
{
printf("%d ",arr[i]);
}
putchar(10);
}
int main(int argc, const char *argv[])
{
int a[] = {1,2,3,4,5,6,7,8,9,10};
myOrder(a,sizeof(a)/sizeof(a[0]));
return 0;
}
练习:编写函数,实现冒泡排序
#include <stdio.h>
void Sort(int a[],int length)
{
int i,j;
for(i = 0 ; i < length - 1;i++)
{
for(j = 0 ; j < length - 1 - i;j++)
{
if(a[j] < a[j+1])
{
#if 0
int t = a[j];
a[j] = a[j+1];
a[j+1] = t;
#endif
a[j] = a[j] + a[j + 1];
a[j + 1] = a[j] - a[j + 1];
a[j] = a[j] - a[j + 1];
}
}
}
}
void Print(int a[],int length)
{
int i;
for(i = 0 ; i < length;i++)
{
printf("%d ",a[i]);
}
putchar(10);
}
int main(int argc, const char *argv[])
{
int a[10] = {0};
printf("请输入10个数字:\n");
int i;
int length = sizeof(a)/sizeof(a[0]);
for(i = 0 ; i < length;i++)
{
scanf("%d",&a[i]);
}
Sort(a,length);
Print(a,length);
return 0;
}
8.5.3 二维数组的传参
#include <stdio.h>
//二维数组传参
//方法1:直接传int a[3][4]
//方法2:传数组指针
void ArrayOrder(int a[3][4])
{
int i,j;
for(i = 0 ; i < 3;i++)
{
for(j = 0 ; j < 4;j++)
{
printf("%-5d",a[i][j]);
}
putchar(10);
}
}
int main(int argc, const char *argv[])
{
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,12,11};
ArrayOrder(a);
return 0;
}
8.5.4 指针数组传参
#include <stdio.h>
//指针数组传递:
//方式1:直接传指针数组
//方式2: 传二级指针
void PointArrayOrder(char *s[],int n)
{
int i;
for(i = 0 ; i < n;i++)
{
printf("s[%d] = %s\n",i,s[i]);
}
}
int main(int argc, const char *argv[])
{
char *s[4];
char s1[] = "hello world";
char s2[] = "hello nanjing";
char s3[] = "hello beijing";
char s4[] = "hello shanghai";
s[0] = s1;
s[1] = s2;
s[2] = s3;
s[3] = s4;
PointArrayOrder(s,4);
return 0;
}
8.6 main的参数
#include <stdio.h>
//main的参数可以不谢,也可以只有一个argc,也可以有两个argc和argv,
//argc:int类型变量,用于保存命令行参数的个数
//argv:是一个指针数组,用来保存多个字符串,用来保存命令行传入的每一个字符串
int main(int argc, const char *argv[])
{
printf("argc = %d\n",argc);
int i;
for(i = 0; i < argc;i++)
{
printf("argv[i] = %s\n",argv[i]);
}
return 0;
}
8.7 指针函数
指针函数:本质是一个函数,函数的返回值是一个地址
#include <stdio.h>
#include <string.h>
//返回值是指针的函数,叫指针函数
char *find_sub(char *str,const char *sub)
{
int str_len = strlen(str);
int sub_len = strlen(sub);
int i;
for(i = 0 ; i < str_len - sub_len + 1 ;i++)
{
if(strncmp(str + i,sub,sub_len) == 0)
{
return str + i;
}
}
return NULL;
}
int main(int argc, const char *argv[])
{
char str[] = "helloworld";
char sub[] = "worl";
char *s = find_sub(str,sub);
if(NULL == s)
{
printf("不存在!\n");
return -1;
}
printf("s = %s\n",s);
return 0;
}
8.7.1 malloc/free
头文件:#include <stdlib.h>
原型:void *malloc(size_t size);
void free(void *ptr);
功能:malloc用于在堆上手动申请size个字节大小的空间
free用于手动释放malloc申请的空间
参数:(malloc):size表示申请size个字节
(free):ptr:表示申请空间的首地址
返回值:(malloc):
成功:返回申请空间的首地址,类型为void*(万能指针)
失败:返回NULL
(free):无返回值
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//返回值是指针的函数,叫指针函数
char *find_sub(char *str,const char *sub)
{
int str_len = strlen(str);
int sub_len = strlen(sub);
int i;
for(i = 0 ; i < str_len - sub_len + 1 ;i++)
{
if(strncmp(str + i,sub,sub_len) == 0)
{
return str + i;
}
}
return NULL;
}
void InitMemory(char **str,char **sub)
{
*str = (char *)malloc(sizeof(char)*128);
*sub = (char *)malloc(sizeof(char)*32);
}
int main(int argc, const char *argv[])
{
//char str[] = "helloworld";
//char sub[] = "worl";
char *str = NULL,*sub = NULL;
InitMemory(&str,&sub);
scanf("%s%s",str,sub);
char *s = find_sub(str,sub);
if(NULL == s)
{
printf("不存在!\n");
return -1;
}
printf("s = %s\n",s);
free(str);
free(sub);
str = NULL;
sub = NULL;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理