五、指针
指针数组
1、定义
本质是数组,里面存放的是指针,
2、格式
存储类型 数据类型 *数组名【元素个数】
int *arr[3];
3、应用
1)用于存放普通变量的地址
Int a = 10, b = 20, c = 30;
int *arr[3] = {&a, &b, &c};
访问 b 的地址:
arr[1] *(arr+1)
访问 b 的值
*arr[1] *(*(arr+1))
2)用于存放二维数组的每一行第一个元素的地址(列地址)
int a[2][3] = {1, 2, 3, 4, 5, 6};
int *arr[2] = {a[0], a[1]};
访问 a[1][2] 的地址:
arr[1]+2 *(arr+1)+2
arr:第一个元素的地址
arr+1:第二个元素的地址
*(arr+1):第二个元素
a[1]:第二个元素,第二行第一列的地址
*(arr+1)+2:第二行第三列的地址
3)用于存放字符串
char str[32] = "hello"; // 存放的是 hello 的字符串
char *str_add = "hello"; // 存放的是 hello 字符串的首地址
printf("%s\n", str);
printf("%s\n", str_add);
printf("%c %c\n", str[1], *(str_add+1));
使用指针数组存放字符串
char *str[3] = {"hello", "world", "wang"};
打印“world”字符串
printf("%s\n", str[1]); // world
printf("%s\n", *(str+1)); // world
printf("%s\n", *(str+1)+2); // rld
打印‘o’字符
printf("%c\n", *(*(str+1)+1)); // o
printf("%c\n", str[1][1]);
printf("%c\n", *(str[1]+1));
字符串写多少都行,没有列数要求,可以当二维数组用,二维数组需要规定列数,指针数组不需要
命令行传参
argv:就是一个指针数组,里面存放的时命令行传递的字符串
argc:表示argv指针数组里面存放的数据的个数,即命令行传递的字符串的个数
六、函数
1、概念
一个完成特定功能的代码模块
2、三要素
功能 参数 返回值
3、格式
存储类型 数据类型 函数名(参数列表) // 形参
{
函数体;return (返回值);
}
注意:
1)没有参数:参数列表可以省略,或者void
2) 没有返回值:return 不写,数据类型是void
3)有返回值:根据返回值的数据类型定义函数的数据类型
4)定义子函数时:可以直接定义在主函数上面,可以直接使用;如果定义在主函数下面的话,要在主函数上面声明子函数
4、声明函数
数据类型 函数名(参数列表);
5、函数调用
1)没有返回值:直接调用:函数名(参数);// 实参,注意数据类型匹配
2)有返回值:如果需要接收返回值,就要定义一个与返回值相同数据类型的变量;
如果不需要,就直接调用函数
#include <stdio.h>
void fun1()
{
printf("hello world\n");
}
void fun2(int a,int b)
{
printf("a+b=%d\n", a+b);
}
int fun3 (int a, int b)
{
return a*b;
}
int fun4(int a, int b);
int main(int argc, char const *argv[])
{
int a = 10;
int b = 5;
fun1();
fun2 ( a, 20);
printf("%d\n", fun3(a, b));
int c = fun4 (a, b);
printf ("%d\n", c);
return 0;
}
int fun4 (int a, int b)
{
return a-b;
}
6、函数传参
1)值传递
单向传递,将实参传递给形参使用,改变形参,实参不会受到影响
#include <stdio.h>
int fun (int num, int sum)
{
num++;
sum++;
return num+sum;
}
int main ()
{
int a = 3;
int b = 4;
int ret = fun (a, b);
printf("%-4d%-4d%-4d\n", a, b, ret);
return 0;
}
2)地址传递
双向传递,在函数中修改形参,实参也会发生变化
#include <stdio.h>
int fun1 (int *num, int *sum)
{
(*num)++;
(*sum)++;
return *num + *sum;
}
int main ()
{
int a = 3;
int b = 4;
int ret = fun1 (&a, &b);
printf("%-4d%-4d%-4d\n", a, b, ret);
}
3)数组传递
参数中存在数组的定义,会认为是指针变量,不会是数组
验证:
#include <stdio.h>
int fun2 (char s[32])
{
s = "hello";
printf("%d\n", sizeof(s));
}
int main()
{
char str[32] = "hello";
fun2(str);
return 0;
}
如果s是数组,就是32;如果是指针就是8
用的时候可以当成指针变量用,也可以当成数组名用
动态内存开辟(开辟堆区空间)
开辟空间
#include <stdlib.h>
void *malloc(size_t size);
功能:在堆区开辟空间
参数:size:开辟空间大小(单位:字节)
返回值:成功:开辟空间的首地址
失败:NULL
释放空间
#include <stdlib.h>
void free(void *ptr);
功能:释放堆区空间
参数:ptr:堆区空间的首地址
返回值:无
free(p);
p = NULL;
示例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *p = (int *)malloc(sizeof(int) * 10);
int *q = p; //p不能移动,一旦移动,释放的时候就找不到了
// 容错判断
if (p == NULL)
{
printf("开辟失败\n");
return -1;
}
printf("开辟成功\n");
// 使用堆区空间
for (int i = 0; i < 10; i++)
{
*(p+i) = i;
}
// 释放堆区空间
free (p);
p = NULL;
return 0;
}
注意:
1、手动开辟堆空间,要注意内存泄漏,
当指针指向开辟堆区空间后,又对指针重新赋值,则没有指针指向开辟堆区空间,就会造成内存泄漏
2、使用完堆区空间后及时释放空间
string函数族
1、strcpy
#include <string.h>
char *strcpy(char *dest, const char *src);
功能:实现字符串的复制
参数:dest:目标字符串首地址
src : 原字符串首地址
把后面的字符串复制到前面的地址里面
返回值:目标字符串首地址
#include <stdio.h> #include <string.h> int main () { char s[32]; printf("%s\n", s); char a[32] = "hello"; strcpy (s, a); printf("%s\n", s); return 0; }
复制包括\0在内的所有字符,注意越界问题
char *strncpy(char *dest, const char *src, size_t n);
功能:复制前几个字符
参数:dest:目标字符串首地址
src : 原字符串首地址
n:字符个数
返回值:目标字符串首地址
#include <stdio.h> #include <string.h> int main () { char s[32]; printf("%s\n", s); char a[32] = "hello"; strncpy (s, a, 3); printf("%s\n", s); return 0; }
s数组没有初始化,所以复制前3个字符里面没有\0所以打印出来前面是hel后面是乱码,使用时对s数组初始化或部分初始化
#include <stdio.h> #include <string.h> int main () { char s[32] = ”world“; printf("%s\n", s); char a[32] = "hello"; strncpy (s, a, 3); printf("%s\n", s); return 0; }
部分初始化之后,最终打印出来的结果为复制部分拼接s数组剩余的部分
2、strlen
#include <string.h>
size_t strlen(const char *s);
功能:计算字符串的实际长度
参数:s:字符串的首地址
返回值:实际长度