//
// main.c
// 指针_注意
//
// Created by admin on 15/7/17.
// Copyright (c) 2015年 admin. All rights reserved.
//
#include <stdio.h>
void test(int *p, int *q);
void test2(int *p, int *q);
int test4(int i, int j, int *cha);
int test5(int i, int j, int *cha, int *ji);
int main(int argc, const char * argv[]) {
/*
1. 通过指针变量来交换两个变量的值
使用场景1:当想在某个函数里面改变外面参数的值,第一个想到的就是指针
*/
int i = 10;
int j = 20;
int sum = 0;
int cha = 0;
int ji = 0;
printf("i = %d, j = %d\n", i,j);
sum = test5(i, j, &cha, &ji);
printf("sum = %d, cha = %d, ji = %d\n", i,cha,ji);
return 0;
}
/*
正确方法,指针传递,temp是中间变量
*/
void test(int *p, int *q) // int *p = &i int *q = &j
{
int temp;
temp = *p;
*p = *q;
*q = temp;
}
/*
错误方法,这里只改变了指针的指向,并没有改变实参的值
*/
void test2(int *p, int *q)
{
int *temp;
temp = p;
p = q;
q = temp;
}
/*
错误方法,基本数据类型作为函数参数是值传递,在函数内修改形参的值不会影响到外面实参的值
*/
void test3(int p, int q)
{
int temp = p;
p = q;
q = temp; // 这里的交换不能影响到外部实参的值
}
/*
2. 函数需要返回多个值
使用场景2:当想某一个函数需要返回多个返回值时,可以通过传入指针的方式返回
即指针得到每个返回值的地址
*/
// 求两个数的和与差
int test4(int i, int j, int *cha)
{
int sum = i + j;
*cha = i - j; //这里要计算值,指针就指向计算出的值的地址,外面就可以通过指针来访问到这个值
return sum;
}
int test5(int i, int j, int *cha, int *ji)
{
int sum = i + j;
*cha = i - j; //这里要计算值,指针就指向计算出的值的地址,外面就可以通过指针来访问到这个值
*ji = i * j;
return sum;
}
//
// main.c
// 指针_和数字的关系
//
// Created by admin on 15/7/17.
// Copyright (c) 2015年 admin. All rights reserved.
//
#include <stdio.h>
int main(int argc, const char * argv[]) {
return 0;
}
void tese()
{
/*
1. 通过指针间接遍历数组
*/
int ages[] = {1,2,3,4,5,6,7};
int length = sizeof(ages)/sizeof(int);
// 首先通过普通方法遍历数组
for (int i = 0; i < length; i++) {
printf("ages[%d] = %d\n", i, ages[i]);
}
// 通过指针遍历数组
int *p = &ages[0];
for (int i = 0; i < length; i++) {
printf("ages[%d] = %d\n", i, *(p+i));
// 这里定义指针p,初始化为数组的第一个元素ages[0],指针变量+1是加上指针变量类型所占用的字节数,这里p+i表示:指针从ages[0]地址开始随着i的增长,开始动态指向数组的第二个、第三个。。。元素。那么在输出的时候*(p+i)就表示取出每个地址的内容,这里即间接根据数组元素地址取出来了数组的内容。
}
// 指针变量+1到底是+多少,是加定义指针变量时的变量类型所占用的字节数
// 例如
double score = 10.1; // double类型占8个字节
double *p1 = &score;
printf("地址:%p", p1);
p1 = p1 + 1;
printf("加1后的地址:%p", p1);
}
void test2()
{
/*
2. 访问数组元素的三种方式
2.1 数组名[角标];
2.2 指针变量名[角标]; // 这里指针变量名表示的是地址,而数组名表示的是数组的第一个元素的地址,可以相互替换
2.3 *(指针变量 + 角标);
*/
}
//
// main.c
// 指针_字符串
//
// Created by admin on 15/7/17.
// Copyright (c) 2015年 admin. All rights reserved.
//
#include <stdio.h>
int main(int argc, const char * argv[]) {
return 0;
}
void test()
{
/*
1. 定义字符串的两种方式
*/
// 1.1. 利用数组
// char name[10] = "abcdefg";
// 特点:字符串里面的内容是可以修改的
// 应用场景:当字符串的内容需要经常修改时候
// 1.2. 利用指针
// char *name = "asdfadsf";
// 特点:字符串里面的内容不可以修改
// 应用场景:当字符串中的内容不需要修改时可以使用指针,可以优化内存空间
/*
2. 两种定义方式的区别
*/
// 2.1 数组定义字符串时
char name1[10] = "kokoko";
char name2[10] = "kokoko"; // name1和name2是分配了不同的内存,可以通过打印变量地址验证
printf("name1地址 = %p\n", name1); // 这里打印地址,而数组名就是表示数组的地址,也可以使用&name[0]替代
printf("name2地址 = %p\n", name2);
// name1地址 = 0x7fff5fbff82e
// name2地址 = 0x7fff5fbff824
name1[0] = 'M';
printf("name1改变第一个元素后:%s", name1); // name1改变第一个元素后:Mokoko
// 2.2 指针定义字符串时
char *name3 = "kokoko";
char *name4 = "kokoko";
printf("name3地址 = %p\n", name3); // name3和name4是分配了同一块内存,可以通过打印指针验证
printf("name4地址 = %p\n", name4); // 这里打印的是地址,而指针就代表地址,所以可以直接打印
// name3地址 = 0x100000f3c
// name4地址 = 0x100000f3c
// *name3[0] = 'M'; // 这样的写法是错误的,指针定义的字符串不能改变其中某一个元素的值
// printf("name3首元素改变后:%c", name3);
/*
3. 字符串数组的两种定义形式
*/
// 3.1 字符串数组定义一
char name5[2][5] = {
"kokok",
"lplpl"
};
printf("%c\n", name5[1][1]); // 这里只能通过角标打印某一个,如果要打印“kokok”字符串,需要遍历打印
// 3.2 指针数组定义
char *name6[5] = {
"kokok",
"lplpl"
};
printf("%s\n", name6[1]); // 这里可以通过角标打印一个字符串
}
//
// main.c
// 指针_返回指针的函数
//
// Created by admin on 15/7/17.
// Copyright (c) 2015年 admin. All rights reserved.
//
#include <stdio.h>
char* test();
int main(int argc, const char * argv[]) {
char *result =test();
printf("%s", result); // s% 是根据给出的地址依次向后取值知道遇到\0停止
return 0;
}
/*
1. 返回指针的函数的格式
返回值类型 函数名(形参类型 形参,形参类型 形参,...)
{
函数体;
}
*/
char* test()
{
char *name = "kokok";
return name;
}
//
// main.c
// 指针_指向函数的指针
//
// Created by admin on 15/7/17.
// Copyright (c) 2015年 admin. All rights reserved.
//
#include <stdio.h>
// 注意: 函数名就是函数的地址, 数组名就是数组的地址
int sum(int i, int j);
int main(int argc, const char * argv[]) {
return 0;
}
/*
1. 指向函数的指针定义格式
void (*pointer)();
注:
(*pointer): 固定格式,这样写代表定义出来的指针是指向函数的指针
左边 void: 代表指针变量pointer将来指向的函数没有返回值,当然可以有返回值类型
右边 (): 代表指针变量pointer将来指向的函数没有形参,当然可以有
*/
/*
2. 指向一个没有返回值,无形参的函数
*/
// 定义函数
void test ()
{
printf("这是测试函数!");
}
//// 定义指向test函数的指针p
//
// void (*p)();
// p = test; // 指针p指向函数test,函数名后千万不能写括号
//
// *p == test; // P 指向 test,*p就是取指针的内容,即test()
//
//// 通过指针p 调用函数test
// test(); // 直接调用
// (*p)(); // 间接调用,P 指向 test,*p就是取指针的内容,即test()
// p(); // 间接调用
//
// /*
// 3. 指向一个有返回值,有形参的函数
// */
//// 定义函数
int sum(int i, int j)
{
return i + j;
}
//// 定义指向sum函数的指针q
//
// int (*q)(int i, int j);
//
// q = sum; // 指针p指向函数test,函数名后千万不能写括号
//
//// *q == sum; // P 指向 test,*p就是取指针的内容,即test()
//
//// 通过指针q 调用函数sum
// int result = sum(10, 20); // 直接调用
// int result1 = (*q)(10,20); // 间接调用
// int result2 = q (10,20); // 间接调用
// printf("result = %d", result);
// printf("result1 = %d", result1);
// printf("result2 = %d", result2);
//
// main.c
// 指针_指向函数的指针(练习)
//
// Created by admin on 15/7/18.
// Copyright (c) 2015年 admin. All rights reserved.
//
#include <stdio.h>
void upper(char *p);
void change(char *str, void (*p)(char*));
int main(int argc, const char * argv[]) {
/*
实现功能:从键盘输入一句英文,单词之间用空格隔开,将所有单词首字母转换成大写,用回调函数实现
*/
// 步骤:
// 1. 提示用户从键盘输入一句英文,单词之间用空格隔开
printf("请输入一句英文,单词间用空格隔开:\n");
// 2. 接收用户输入的英文,使用 gets()函数
char container[100];
gets(container);
printf("转换前:%s\n", container);
// 3. 转换英文单词首字母为大写
change(container, upper);
printf("转换后:%s\n", container);
return 0;
}
/*
转换字母为大写的函数
*/
// 因为要改变外部实参的值,所以这里定义形参为指针,使用时候传入存储字符串的数组名,即数组的地址即可
void upper(char *p)
{
char temp = *p; // 首先将指针所指的内容取出,赋值给一个变量,然后对其操作
if (temp >= 'a' && temp <= 'z') {
temp = temp - ('a' - 'A'); // 根据ASCII 特点将小写转换为大写
}
*p = temp; // 将更改后的内容重新赋值给指针指向的存储空间
}
/*
转换整句英文格式的函数(使用回调函数)
*/
// 回调函数:将函数1以参数形式传入函数2,当函数2用到时,直接调用
// 在c语言中用到回调函数,就要想到指向函数的指针
void change(char *str, void (*p)(char*)) // void (*p)(char*) 为指向函数的指针
{
(*p)(str); // 先将首字母变为大写
while (*str != '\0') { // while条件判断字符串是否结束 如果指向内容为‘\0’就跳出结束
if (*str == ' ') { // 判断是否为空格
str++; // 指向空格的下一个字符
(*p)(str); // 调用函数,通过指向函数的指针
}else{
str++;
}
}
}
//
// main.c
// 指针_自定义字符串复制、拼接、比较
//
// Created by admin on 15/7/18.
// Copyright (c) 2015年 admin. All rights reserved.
//
#include <stdio.h>
#include <string.h>
char* myStrcpy(char *dest, const char *src); // const 标注的变量为固定值,值不能通过重新赋值而改变
char* myStrcpy1(char *dest, const char *src);
char* myStrcat(char *dest, const char *src);
char* myStrcat1(char *dest, const char *src);
int myStrcmp(const char *str1, const char *str2);
int main(int argc, const char * argv[]) {
char ages[5] = "koko";
char name[5] = "koo";
/*
printf("复制前 ages = %s", ages);
myStrcpy(ages, name);
printf("复制后 ages = %s", ages);
*/
/*
printf("复制前 ages = %s", ages);
myStrcpy1(ages, name);
printf("复制后 ages = %s", ages);
*/
/*
printf("拼接前 ages = %s", ages);
myStrcat(ages, name);
printf("拼接后 ages = %s", ages);
*/
/*
printf("拼接前 ages = %s", ages);
myStrcat1(ages, name);
printf("拼接后 ages = %s", ages);
*/
int result = myStrcmp(ages, name);
printf("result = %d", result);
return 0;
}
/*
1. 字符串拷贝
*/
// 1.1 系统自带 字符串拷贝函数
// strcpy(dest, src) 和 strncpy(dest, src, len) 和 strlcpy(dest, src, len)
// 其中参数len 代表要拷贝的字符串的长度 具体区别见博客
/**
* 1.2 自定义字符串拷贝函数(使用指针)
*
* @param dest 目标容器
* @param src 被拷贝的字符
*
* @return 拷贝后的地址
*/
char* myStrcpy(char *dest, const char *src) // 定义俩指针,传入时数组名作为地址传入
{
char *cp = dest; // 定义一个指针保存字符串的首地址
while (*src != '\0') { // 循环遍历取出src中的所有字符
*dest = *src; // 将src所指向地址的值取出来赋值给dest所指向地址的值,即把src中的字符拷贝到dest中
src++; // 指针向后移动
dest++; // 指针向后移动
}
*dest = '\0'; // dest所指的字符后加\0结束
return cp; // 返回字符串首地址
}
//进一步对myStrcpy优化
char* myStrcpy1(char *dest, const char *src)
{
char *cp = dest; // 定义一个指针保存字符串的首地址
while ((*dest++ = *src++)); // 循环遍历取出src中的所有字符
return cp;
}
/*
2. 字符串拼接
*/
// 2.1 系统自带 字符串拼接函数
// strcat(dest, src) 和 strncat(dest, src, len) 和 strlcat(dest, src, len)
// 其中参数len 代表要拼接的字符串的长度 具体区别见博客
/**
* 2.2 自定义字符串拼接函数(使用指针)
*
* @param dest 目标容器
* @param src 被拼接的字符
*
* @return 拼接后的地址
*/
char* myStrcat(char *dest, const char *src)
{
char *cp = dest; // 定义指针保存字符串的首地址
while (*dest != '\0') { // 移动dest指针到外面实参ages的字符串结尾(\0)
dest++;
}
while (*src != '\0') { // 拷贝name中的字符到ages中
*dest = *src;
src++;
dest++;
}
*dest = '\0'; // 添加字符串结尾标识
return cp; // 返回首地址
}
//进一步对myStrcat优化
char* myStrcat1(char *dest, const char *src)
{
char *cp = dest;
while (*dest) // 移动dest指针到外面实参ages的字符串结尾(\0)
dest++;
while ((*dest++ = *src++)); // 拷贝name中的字符到ages中
return cp;
}
/*
3. 字符串比较
*/
// 3.1 系统自带 字符比较函数
// int strcmp(const char *, const char *) 和 int strncmp(const char *, const char *, size_t)
// 其中参数size_t 代表要比较的字符串的长度 。具体区别见博客
/**
* 3.2 自定义字符串比较函数(使用指针)
*
* @param str1 指向第一个要比较的字符串
* @param src 指向第二个要比较的字符串
*
* @return 比较结果
*/
int myStrcmp(const char *str1, const char *str2)
{
int result = *str1 - *str2; // 俩指针通过数组名指向首元素, 首元素想减,比较是否相等
// 若相减为0,说明首元素相同,即为假。
// 若相减不为0,说明首元素不同,即为真
while (!result && *str1 !='\0') { // !result表示:如果为真才能进入循环,即result为假,就是首元素相同,
// 且此时 两个字符串中都不是结尾符\0 时进入循环
str1++;
str2++; // 指针自加向后移动
result = *str1 - *str2; // 继续相见判断是否跳出循环
}
if (result < 0) { // 判断 result
return -1;
}else if (result > 0)
{
return 1;
}
return result;
}