精华内容
下载资源
问答
  • // 得到与图像像素数目长度一样的一组码本,以便对每个像素进行处理 for (int i=0; i; i++) // 初始化每个码元数目为0 cB[i].numEntries = 0; for (int i=0; i; i++) { cbBounds[i] = 10; // 用于...
  • 第八周

    千次阅读 2017-02-21 17:10:57
    数组最好用宏定义 一维数组在内存中占用字节的长度=数组长度 * sizeof(基类型) 高效的数组初始化方法:#include memset(a, 0, sizeof(a));高效的数组赋值:#...使用这语句时,如果数组a和b的长度不一样

    数组最好用宏定义
    一维数组在内存中占用字节的长度=数组长度 * sizeof(基类型)
    高效的数组初始化方法:

    #include<string.h>
    memset(a, 0, sizeof(a));

    高效的数组赋值:

    #include<string.h>
    memcpy(b, a, sizeof(a));

    memcpy(b, a, sizeof(a));
    使用这条语句时,如果数组a和b的长度不一样,
    a比b长,溢出元素数据会丢失。
    b比a长,不够的部分仍会显示原数据
    简单参数传递给函数call by value
    数组传递给函数call by reference

    #include <stdio.h>
    
    void Input(int x[], int n);
    int Total(int x[], int n);
    int FindMaxValue(int x[], int n);
    int FindMinValue(int x[], int n);
    int main()
    {
        int maxValue, minValue, Score[10], sum;
        Input(Score, 10);
        maxValue = FindMaxValue(Score, 10);
        minValue = FindMinValue(Score, 10);
        sum = Total(Score, 10);
        printf("%d\n",(sum - maxValue - minValue) / 8);
        return 0;
    }
    void Input(int x[], int n)
    {
        int i;
        for(i = 0; i < n; i++)
        {
            scanf("%d", &x[i]);
        }
    }
    int FindMaxValue(int x[], int n)
    {
        int i, maxValue = x[0];
        for(i = 1; i < n; i++)
        {
            if(maxValue < x[i])
            {
                maxValue = x[i];
            }
        }
        return maxValue;
    }
    int FindMinValue(int x[], int n)
    {
        int i, minValue = x[0];
        for(i = 1; i < n; i++)
        {
            if(minValue > x[i])
            {
                minValue = x[i];
            }
        }
        return minValue;
    }
    int Total(int x[], int n)
    {
        int i, sum = 0;
        for(i= 0; i < n; i++)
        {
            sum += x[i];
        }
        return sum;
    }
    

    线性查找,二分法查找

    #include<stdio.h>
    int LinSearch(int num[], int x, int n);//线性查找
    int Binsearch(int num[], int x, int n);//二分查找(分治思想),要求查找表有序
    int main()
    {
        return 0;
    }
    int LinSearch(int num[], int x, int n)
    {
        int i = 0;
        for(i = 0; i < n; i++)
        {
            if(x == num[i])
            {
                return i;
            }
        }
        return -1;
    }
    int Binsearch(int num[], int x, int n)
    {
        int low = 0, high = n - 1, mid;
        while (low < high)
        {
            //mid = (low + high) / 2;
            mid = low + (high - low) / 2;//防止数值溢出
            if (x < num[mid])
            {
                high = mid - 1;
            }
            else if (x > mum[mid])
            {
                low = mid + 1;
            }
            else
            {
                return mid;
            }
        }
        return -1;
    }

    交换排序

    #include <stdio.h>
    void ChangeSort(int x[], int n);
    int main()
    {
        int x[4] = {4, 2, 5, 6};
        ChangeSort(x, 4);
        return 0;
    }
    void ChangeSort(int x[], int n)
    {
        int i, j, temp;
        for (i = 0; i < n - 1; i++)
        {
            for (j = i + 1; j < n - 1; j++)
            {
                if (x[i] > x[j])
                {
                    temp = x[i];
                    x[i] = x[j];
                    x[j] = temp;
                }
            }
        }
    }

    选择法排序

    #include <stdio.h>
    void SelectionSort(int x[], int n);
    int main()
    {
        int x[] = {61, 83, 88, 87, 84};
        SelectionSort(x, 5);
        return 0;
    }
    void SelectionSort(int x[], int n)
    {
        int i, j, k,  temp;
        for (i = 0; i < n - 1; i++)
        {
            k = i;
            for (j = i + 1; j < n - 1; j++)
            {
                if (x[k] > x[j])
                {
                    k = j;
                }
            }
            if (k != i)
            {
                temp = x[i];
                x[i] = x[k];
                x[k] = temp;
            }
        }
    }

    冒泡法排序

    #include <stdio.h>
    void BubbleSort(int x[], int n);
    int main()
    {
        int x[] = {61, 83, 88, 87, 84};
        BubbleSort(x, 5);
        return 0;
    }
    void BubbleSort(int x[], int n)
    {
        int i, j, temp;
        for (i = 0; i < n - 1; i++)
        {
            for (j = 1; j < n - i; j++)
            {
                if (x[j] < x[j - 1])
                {
                    temp = x[j];
                    x[j] = x[j - 1];
                    x[j - 1] = temp;
                }
            }
        }
    }

    筛法求素数

    //筛法求素数
    #include<stdio.h>
    #include <math.h>
    
    #define N 100
    void ShiftPrime(int a[], int n);
    void PrintPrime(int a[], int n);
    int main()
    {
        int a[N + 1];
        ShiftPrime(a, N);
        PrintPrime(a, N);
        return 0;
    }
    void ShiftPrime(int a[], int n)
    {
        int i, j;
        for (i = 2; i <= n; i++)
        {
            a[i] = i;
        }
        for (i = 2; i <= sqrt(n); i++)
        {
            for (j = i + 1; j <= n; j++)
            {
                if (a[i] != 0 && a[j] != 0 && a[j] % a[i] == 0)
                {
                    a[j] = 0;
                }
            }
    
        }
    }
    void PrintPrime(int a[], int n)
    {
        int i;
        for (i = 2; i <= n; i++)
        {
            if (a[i] != 0)
            {
                printf("%d\t", a[i]);
            }
        }
         printf("\n");
    }
    

    鲁智深吃馒头(约瑟夫环)

    据说,鲁智深一天中午匆匆来到开封府大相国寺,想蹭顿饭吃,当时大相国寺有99个和尚,只做了99个馒头,智清长老不愿得罪鲁智深,便把他安排在一个特定位置,之后对所有人说,从我开始报数(围成一圈),第5个人可以吃到馒头(并退下),按照这个公平的方法,所有和尚都吃到了馒头,唯独鲁智深没有吃上。请问他在哪个位置?

    #include<stdio.h>
    #define N 100
    int main()
    {
        int a[100 + 1];
        int b = 0;//the number of steamed bread
        int num = 1;//现在报的数
        int i;
        for (i = 1; i <= N; i++)
        {
            a[i] = i;
        }
        while (b < 99)
        {
            for (i = 1; i <=N; i++)
            {
                if(a[i] != 0)//如果这个人没有吃馒头
                {
                    if (num % 5 == 0)//报的数为5
                    {
                        b++;//馒头数加一
                        a[i] =0;//此人吃馒头
                        printf("%d号吃第%d个馒头\n",i,b);
                    }
                    num++;
                }
            }
        }
        for( i=1; i<N; i++ )
        {
            if(a[i]!=0)
            {
                printf("\n\n鲁智深是第%d个人\n",i);
            }
        }
        return 0;
    }

    文曲星猜数

    XAXB
    A前表示数和位置皆对
    B前表示数对,位置不对

    #include<stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    
    void MakeDigit(int a[]);
    int InputGuess(int b[]);
    int IsRightPosition(int magic[], int guess[]);
    int IsRightDigit(int magic[], int guess[]);
    int main()
    {
        int a[10], b[4];
        int lever;
        int count;
        int rightPosition, rightDigit;
        MakeDigit(a);
        printf("How many times do you want to guess\n");
        scanf("%d", &lever);
        count = 0;
        do
        {
            printf("NO. %d of %d times \n", count, lever);
            printf("Input your guess:\n");
            if (InputGuess(b) == 0)continue;
            count++;
            rightPosition = IsRightPosition(a, b);
            rightDigit = IsRightDigit(a, b) - rightPosition;
            printf("%dA%dB\n", rightPosition, rightDigit);
        }while (rightPosition != 4 && count < lever);
        if(rightPosition == 4)
        {
            printf("Congratulation your guess the right number at NO.%d\n", count);
        }
        else
        {
            printf("Sorry you haven't guess the right number, see you next time");
        }
        printf("Correct answer is : %d %d %d %d",a[0], a[1], a[2], a[3]);
        return 0;
    }
    //void MakeDigit(int a[])
    //{
    //    srand(time(NULL));
    //    a[0] = rand() % 10;
    //    do
    //    {
    //        a[1] = rand() % 10;
    //    } while (a[0] == a[1]);
    //    do
    //    {
    //        a[2] = rand() % 10;
    //    } while (a[2] ==a[0] || a[2] == a[1]);
    //    do
    //    {
    //        a[3] = rand() % 10;
    //    } while (a[3] ==a[0] || a[3] == a[1] || a[3] == a[2]);
    //}
    void MakeDigit(int a[])
    {
        int i, j ,temp;
        //srand(time(NULL));
        for (i = 0; i < 10; i++)
        {
            a[i] = i;
        }
        for (i = 0; i < 10; i++)
        {
            j = rand() % 10;
            temp = a[j];
            a[j] = a[i];
            a[i] =temp;
        }
    }
    int InputGuess(int b[])
    {
        int i,ret;
        for (i = 0; i < 4; i++)
        {
            ret = scanf("%1d", &b[i]);
            if (ret != 1)
            {
                printf("Input data type error \n");
                fflush(stdin);
                return 0;
            }
        }
        if (b[0] == b[1] || b[0] == b[2] || b[0] == b[3] || b[1] == b[2] || b[1] == b[3] || b[2] == b[3])
        {
            printf("The number must be different from each other, please input again\n");
            return 0;
        }
        else
            return 1;
    }
    int IsRightPosition(int magic[], int guess[])
    {
        int rightPosition = 0;
        int j;
        for (j = 0; j < 4; j++)
        {
            if (guess[j] == magic[j])
            {
                rightPosition++;
            }
        }
        return rightPosition;
    }
    int IsRightDigit(int magic[], int guess[])
    {
        int rightDigit;
        int j, k;
        for (j = 0; j < 4; j++)
        {
            for (k = 0; k < 4; k++)
            {
                if (magic[j] == guess[k])
                {
                    rightDigit++;
                }
            }
    
        }
        return rightDigit;
    }
    

    大数存储

    展开全文
  • 之后分别从栈中pop比较一不一样,找到第一个不一样的后面一个节点就行。 思路三,先比较两个链表,长度,长的先走几个节点,保证两个节点到各自链表尾部的长度是一样的。也不用单独开辟内存空间,一...

    思路一:题目求第一个公共节点,这表明,不止一个公共节点,那么先让一条链表的所有节点入栈,在遍历另一个链表,看看栈中有没有这个元素,有就说明是第一个。
    思路二:可以分别让两个链表入栈,(思路就是尾部一定是一样的)。之后分别从栈中pop比较一不一样,找到第一个不一样的后面一个节点就行。
    思路三,先比较两个链表,长度,长的先走几个节点,保证两个节点到各自链表尾部的长度是一样的。也不用单独开辟内存空间,一直循环下去就行。

    # -*- coding:utf-8 -*-
    # class ListNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.next = None
    class Solution:
        def FindFirstCommonNode(self, pHead1, pHead2):
            # write code here
            if not pHead1 or not pHead2:
                return None
            res1=[]
            res2=[]
            cur1=pHead1
            cur2=pHead2
            while cur1:
                res1.append(cur1.val)
                cur1=cur1.next
            while cur2:
                if cur2.val in res1:
                    return cur2
                cur2=cur2.next
            return None
    

    方法三:

    class Solution:
        def FindFirstCommonNode(self, pHead1, pHead2):
            # write code here
            if not pHead1 or not pHead2:
                return None
            cur1=pHead1
            cur2=pHead2
            cnt1=0
            cnt2=0
            while cur1:
                cnt1+=1
                cur1=cur1.next
            while cur2:
                cnt2+=1
                cur2=cur2.next
            node1=pHead1
            node2=pHead2
            step=abs(cnt1-cnt2)
            
            if cnt1>=cnt2:
                while step>0:
                    node1=node1.next
                    step-=1
            else:
                while step>0:
                    node2=node2.next
                    step-=1
            while node1:
                if node1==node2:
                    return node1
                else:
                    node2=node2.next
                    node1=node1.next
            return None
    
    展开全文
  • 数组和链表

    2021-01-06 13:41:02
    线性表数据结构:就是数据排列像一线一样的结构,只有前后的方向, 连续的内存空间:就是存储数据在内存中的地址是连续的,例如存储一个长度为3的整数int[3]数组,int类型数据在内存中占4个字节,...

     

    目录

    前言

    数组

    链表

    总结


    前言

    大家好,本篇我们主要分享数据结构中最基础的两种数据结构数组链表,我们话不多开,正文开始!

    正文

     

    数组

    我们先来说说数组结构,什么是数组?数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。

    1. 线性表数据结构:就是数据排列像一条线一样的结构,只有前后的方向,

    2. 连续的内存空间:就是存储数据在内存中的地址是连续的,例如存储一个长度为3的整数int[3]数组,int类型数据在内存中占4个字节,所以数组的起止地址为1000~1003,1004~1007,1008~1010是连续的。

    数组优点:

    1. 随机访问,通过索引访问数组数据,由于是连续的内存空间,所以支持随机访问,通过下标随机访问的时间复杂度是O(1)。

    2. 查找快,可以通过数组的所以快速定位到要查找的数据

    3. 支持动态扩容,当我们开始预定义数组的长度是10,当数组存储了10个数据后,我们继续添加第11个数据的时候,我们就需要重新分配一块更大的空间,将原来的数据复制过去,然后再将新的数据插入

    数组缺点:

    1. 插入、删除数据慢,因为数组连续的内存空间,所以在插入一个数据的时候会使插入位置后面的每个数据都向后位移一个位置,所以比较低效,删除同理,会使删除位置之后的每个数据向前位移一个。

    2. 操作不当会引起数组的访问越界问题,比如我定义的数组长度是10,索引长度从0~9,但是当我访问位置10的时候,超过数组长度,所以会发生越界问题

    3. 查询某个元素是否存在时需要遍历整个数组,耗费 O(n) 的时间。

    4. 删除和添加某个元素时,同样需要耗费 O(n) 的时间

    习题练习

    数组介绍基本就这些了,虽然数组比较简单,但确是面试中最容易的超高频数据结构,我找了几个在经常在面试中出现的题目,下面和大家分享一下

    /** * LeetCode344题目描述: * 输入字符数组 char[a,b,c,d] ,输出字符数组cahr[d,c,b,a],实现数组翻转 * 不要给另外的数组分配额外的空间,你必须原地修改输入数组、 * 使用 O(1) 的额外空间解决这一问题 * 解题思路: * 两个指针,一个指向字符串的第一个字符 a,一个指向它的最后一个字符 d, * 然后互相交换。交换之后,两个指针向中央一步步地靠拢并相互交换字符,直到两个指针相遇。 * */public class CharDemo1 {    public static void main(String[] args) {        char[] arr={'a','b','c','d'};        char[] reverse = reverse(arr);        //输出reverse打印出d c b a        System.out.println(reverse);    }    //left就是左指针,right是右指针    public static char[] reverse(char[] arr){        int size=arr.length;        for (int left = 0, right = size-1; left < right; ++left, --right) {            char tmp = arr[left];            arr[left] = arr[right];            arr[right] = tmp;        }        return arr;    }}

    链表

    相比数组,链表是有一系列不必在内存中连续的结构组成,是一种稍微复杂一点的数据结构。但是他和数组都是面试中经常出现的,先来说说什么是链表?与数组相比,数组需要一块连续的内存空间来存储,假如申请100MB大小的数组,但是连续内存空间不够,就无法创建数组,但是链表并不需要一块连续的内存空间,它通过“指针”将一组零散的内存块串联起来使用,所以如果我们申请 100MB内存空间如果不是连续的内存空间,就是可以的

    所以链表是通过指针将一组零散的内存块串联在一起的一种数据结构,链表又分为:单链表、双向链表和循环链表。我们首先来看最简单、最常用的单链表。

     

    单链表结构

    单链表每一个节点均包含数据元素和指向下一个节点的指针元素,指针元素存储的是下一个节点的地址信息,最后一个节点的next指针指向NULL,类似下图结构:

     

    我们把第一个结点叫作头结点,把最后一个结点叫作尾结点。

    插入节点和删除节点图如下:

    单链表优点:

    1. 插入和删除快,和数组相比,链表的插入和删除不需要其他的节点的位移,我们只需要修改节点之间的next指针指向,就可以完成插入和删除,时间复杂度已经是 O(1)

    2. 链表能灵活地分配内存空间。

    单链表缺点:

    1. 查询相比数组低效,因为链表的内存地址不是连续的,所以不能像数组一样通过索引随机访问,所以链表在查找某个节点时候只能通过从头遍历链表节点来查找。

    2. 查询第 n 个元素需要 O(n) 时间

    双向链表结构:

    与单点链表相比,双向链表增加了一个指向前面节点的pre指针,同事需要额外的一个空间来存储前驱结点的地址。所以,如果存储同样多的数据,双向链表要比单链表占用更多的内存空间,双向链表结构如下:

    双向链表优点:

    1. 支持双向遍历,双向链表增加了操作的灵活性

    循环链表结构:

    单链表的循环链表结构就是在单链表的基础上将尾节点的next指针指向头节点

    也可以是双向循环链表,头节点的pre指针指向尾节点,尾节点的next指针指向头节点。

    循环链表的优点是从链尾到链头比较方便,一般用于处理具有循环结构的数据时采用循环列表。

    单链表和双向链表的对比:

    1. 查询对比:查询某个元素的时间复杂度都是O(n),都需要从头或者从尾开始遍历链表。

    2. 删除某个元素的时候,单链表第一次查询找到该元素,但是他不能控制前一个节点的next的指向,例如删除q节点,为了找到前驱结点,我们还是要从头结点开始遍历链表,直到 p->next=q,然后通过修改p节点的next指针指向被删除节点q的next指针指向的节点。然而双向链表中的结点已经保存了前驱结点的指针,不需要像单链表那样遍历,单链表删除操作需要 O(n) 的时间复杂度,而双向链表只需要在 O(1) 的时间复杂度内就可以搞定,插入操作同理,单链表时间复杂度是O(n),双链表是O(1)。

    3. 如果都是有序的链表查找,就查找速率而言,双向链表也比但链表要高,因为双向链表可以记录上次的查询值,通过对比值的大小,通过前驱指针和后驱指针双向遍历,但是但链表只能从头遍历。

    习题练习

    LeetCode第25题,链表翻转,以下解题思路和图来自力扣,附加一些本盟主的理解和注释,方便大家更好的理解。题目描述:给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。代码如下:

    package com.example.demo.testdemo.listdemo;import java.util.HashMap;
    /** * 解题思路: * 1.链表分为已翻转区,待翻转区,未翻转区 * 2.每次翻转前,需要通过k值确定翻转的范围 * 3.需要记录翻转的前驱和后继,以便翻转完后,把已翻转的和未翻转的连接起来 * 4.初始化需要两个变量,pre和end,pre代表翻转链表的前驱,end代表翻转链表的后继 * 5.通过k次循环,end到达末尾,记录带翻转的末尾next=end.next。 * 6.翻转列表,将三部分连接起来,重置pre和end的指针,然后进入下一次翻转 * 7.特殊情况,当翻转部分长度不足k时,在定位end完成后end==null已经到达末尾,说明题目已完成,直接返回即可 * 8.时间复杂度为 O(n*K) 最好的情况为 O(n) 最差的情况为O(n^2)O * 9.空间复杂度为O(1); */public class demo1 {    public static void main(String[] args) {        //我们先定义单链表结构1->2->3->4->5->null,那么如果k传2,        // 最终返回的结构就是2->1->4->3->5        ListNode listNode1=new ListNode(1);        ListNode listNode2=new ListNode(2);        ListNode listNode3=new ListNode(3);        ListNode listNode4=new ListNode(4);        ListNode listNode5=new ListNode(5);        listNode1.next=listNode2;        listNode2.next=listNode3;        listNode3.next=listNode4;        listNode4.next=listNode5;        listNode5.next=null;        //返回的listNode,结构2->1->4->3->5,我们先看reserveGroup方法        ListNode listNode = reserveGroup(listNode1, 2);    }    //反转函数    public static ListNode reserveGroup(ListNode head,int k){        //dummy保存链表head的前驱节点        ListNode dummy=new ListNode(0);        //讲dummy的next节点指向head,此时链表结构dummy->1->2->3->4->5        dummy.next=head;        //初始化pre和end指针的位置,如 **图1**        ListNode pre = dummy;        ListNode end = dummy;        //每次循环,判断end后面还有节点,才有继续翻转的可能。        while(end.next != null){            //看end后继节点是不是够k个,并且每次循环end指针向后移动,            // k次之后end指向3,如 **图2**            for(int i=0;i<k && end != null; i++){                end=end.next;            }            //如果end为空,说明链表的长度不够k个,不需要翻转直接跳出循环            if(end == null){                break;            }            //记录开始翻转的位置            ListNode start = pre.next;            //保存翻转之后的后继节点            ListNode next=end.next;            //将要翻转的链表的后继节点置空,如**图3**            end.next=null;            //翻转链表            ListNode reverse = reverse(start);            //连接前驱节点和后继节点,如**图4**,**图5**            pre.next=reverse;            start.next=next;            //重置pre和end的位置,准备开始下一轮翻转,如**图5**,然后开始下一轮的翻转            pre=start;            end=pre;        }        //返回翻转完后链表的头节点        return dummy.next;    }    //翻转方法,通过三个指针,pre、cu人、next指针进行翻转,这个通过网上找的**图6**,    // 来给大家解释,图中的意思就是代码描述的意思,翻转本身并不复杂。    public static ListNode reverse(ListNode head){        ListNode pre =null;        ListNode cur=head;        while (cur != null){            ListNode next=cur.next;            cur.next=pre;            pre=cur;            cur=next;        }        return pre;    }    //我们定义简单的链表结构,每个节点有节点的data和节点的后继节点next组成    static class ListNode{        int data;        ListNode next;        ListNode(int data){            this.data=data;        }    }}

     

    以下图解均来自leetCode,我把图分开来解释,方便大家理解。

    图1:

    图2:

    图3:

    图4:

    图5:

    图6:


     

    总结

     

    本期我们主要讲解了数组和链表的基本结构和简单的一些对比,通过一些面试经常问的算法题让大家对其结构能有更加深入的理解,有关数组和链表的算法题还有很多,感兴趣的同学可以多去leetCode刷刷题,那么,我们下期见!

     

    展开全文
  • 计算机是如何执行程序的,可以用下面的图来形象的表示一下: 内存中存放指令和数据。CPU的EIP寄存器存放下一个CPU指令存放的内存地址,当CPU执行...指令的长度也是不一样的。 可以由我们来看一下 CPU中的几个基

    计算机是如何执行程序的,可以用下面的图来形象的表示一下:


    内存中存放指令和数据。CPU的EIP寄存器存放下一个CPU指令存放的内存地址,当CPU执行完当前的指令后,从EIP寄存器中读取下一条指令的内存地址,然后继续执行。


    EIP寄存器中的指令地址是递增的,但是它可以由CALL ,RET ,JMP 来更改的。指令的长度也是不一样的。

    可以由我们来看一下 CPU中的几个基本寄存器。


    内存指令:MOV  PUSH POP 等。来看一下这几个命令执行的时候的具体操作


    这里需要了解的情况是,栈是由高地址向低地址增长的,所以push的时候是 subl ,pop的时候是 addl。

    问题:在一段简单的代码上分析计算机是如何工作的

    首先写一段 测试代码:example.c,   放在/linux系统分析文件夹下面,代码:
    #include <stdio.h>  
      
    int g(int x)  
    {  
        return x + 3;  
    }  
      
    int f(int x)  
    {  
        return g(x);  
    }  
      
    int main(void)  
    {  
        printf("%d",f(8) + 1);
        return f(8) + 1;    
    }  

    第一步任务:使用exampe 的c代码分别生成.cpp , .s ,.o, 和ELF 可执行文件,并加载运行。分析.s汇编代码在CPU的执行过程。


    先介绍下 gcc 的编译过程:
            一般情况下,c程序代码的编译过程为:1.预处理 -> 2.编译成汇编代码->3.汇编成目标代码 ->4.链接。

            1.预处理:使用-E参数,输出文件后缀 为 .cpp          

    gcc -E -o example.cpp example.c 

                   执行之后
                   
             
    wc example.c example.cpp 


                  执行之后:
                  
              预处理的作用:文件包含、宏替换、条件编译等
                  1、文件包含
                           预处理指令#include用于包含头文件,有两种形式:#include <xxx.h>,#include "xxx.h",要将头文件的定义在保护条件中。目的是为了防止重复包含头文件
                  2、宏替换 
                           宏定义中需要注意的情况:

                                1. 一般在宏定义的结尾不加分号。

                                       我们在使用的时候,要加上分号,像我们平时写语句一样。

                                2. 注意加括号。

                                   在有参数的空定义中,如果含有数值运算,那么就要在“宏整体”和“宏参数”两端都要加上括号。   如:#define max(a, b) ((a)+(b)); 

                                3. 注意空格。

                                   在有参数的宏定义中,注意“宏名称”和“参数列表”之间不能有空格。  如:#define max (a, b) ((a)+(b));  在"max”和”(a, b)”之间不能有空格。

                                4. 不要使用有副作用的参数区调用宏。 常见的有副作用的参数有:a++,getchar()等。 

                                  如:宏定义为#define max (a, b) ((a)+(b));  那么使用max(i++, j++)调用该宏,会造成 i 或 j 中的一个值增加2,而不是我们期望的 1。

                                5. 可以使用编译器选项 添加宏 和 移除宏。 我使用的是gcc,添加宏的指令是”-D”,移除宏的指令是”-U”。

                                6. 宏参数替换的时候,不会替换字符串中的字符。即不会替换双引号之间的字符,其他的都会被替换,包括单引号之间的。


                                 用实际值替换用“#define”定义的字符串。

             3、条件编译

                             一般情况下,在进行编译时对源程序中的每一行都要编译,但是有时希望程序中某一部分内容只在满足一定条件时才进行编译,如果不满足这个条件,就不编译这部分内容,这就是条件编译。条件编译主要是进行编译时进行有选择的挑选,注释掉一些指定的代码,以达到多个版本控制、防止对文件重复包含的功能。#if,#ifndef,#ifdef,#else,#elif,#endif是比较常见条件编译预处理指令,可根据表达式的值或某个特定宏是否被定义来确定编译条件。



            2.编译成汇编代码:预处理文件 -->  汇编代码。

              1). 使用-x参数说明根据指定的步骤进行工作,cpp-output指明从预处理得到的文件开始编译
              2). 使用-S说明生成汇编代码后停止工作gcc -x cpp-output -S -o gcctest.s gcctest.cpp 也可以直接编译到汇编代码
    gcc -S gcctest.c
              代码:
    gcc -x cpp-output -S -o example.s example.cpp 
    rm example.cpp  example.s
    gcc -S example.c 


              执行之后:

            3.编译成目标代码


                  1) 汇编代码->目标代码:gcc - x assembler  -c  gcctest.s  
                  2) 直接从源代码编译成目标代码:gcc -c gcctest.c
                  3) 使用汇编器生成目标代码:as -o gcctest.o gcctest.s (从汇编代码使用汇编器 生成目标代码)同一
             代码:
    gcc -x assembler -c example.s
    gcc -c example.c
    as -o example.o example.s


             执行过程:

        

               4.编译生成可执行文件。

                  1) 目标代码->执行代码   gcc -o gcctest gcctest.o(格式) 如果有多个目标文件生成可执行文件,则 gcc -o gcctest gcctest1.o  gcctest2.o  gcctest3.o ...   
                  2 )  直接由源代码生成可执行文件  gcc -o  gcctest gcctest.c (跳过了 汇编代码 和 目标代码)
                   代码:
    gcc -o example example.o
    gcc -o example example.c

     
                  执行过程:

              5.运行可执行文件

                 命令:在 可执行文件的文件夹下面执行./gcctest  
                 代码:
    ./example



                6.gcc 的高级选项

                     1)  -Wall  打开所有警告信息。 使用方法:gcc -Wall + 其他 (W一定要大写)
                     2)  优化编译选项: 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高 
                          -O0  不进行任何优化。
                          -O1   缺省值
                         -O2  进行优化
                          -O3  优化程度最高
                     测试例子: 源代码:

    #include <stdio.h>
    #include <math.h>
    int main(void)
    {
    	int i,j;
    	double k=0.0,k1=k2=k3=1.0;
    	for (i=0;i<50000;i++)
    	for (j=0;j<50000;j++)
    	{
    		k+=k1+k2+k3;
    		k1 += 0.5;
    		k2 += 0.2;
    		k3 = k1+k2;
    		k3 -= 0.1;
    	}
    	return 0;
    }
                          使用不同优化选项,分别生成不同的可执行文件。
     gcc -O0 -o m0 example.c
     gcc -O1 -o m1 example.c
     gcc -O2 -o m2 example.c
     gcc -O3 -o m3 example.c
    执行命令后,生成4个不同的可执行的文件:

    使用time 命令统计程序的运行,看下面图片:

    验证了我们的O0没有进行优化,这里我们可以看出来没有优化和优化过的可执行文件的执行时间差距还是特别大的,O3的优化最高,执行时间最少。



    任务二:分析 .s汇编代码 在CPU上的执行过程

    看一下 example.s 的代码,用gedit打开,信息如下:
    	.file	"example.c"
    	.text
    	.globl	g
    	.type	g, @function
    g:
    .LFB0:
    	.cfi_startproc
    	pushl	%ebp        //ebp寄存器内容压栈,保存f函数的入口地址   
    	.cfi_def_cfa_offset 8
    	.cfi_offset 5, -8
    	movl	%esp, %ebp     //esp值赋给ebp,设置f函数的栈基址
    	.cfi_def_cfa_register 5
    	movl	8(%ebp), %eax       //将参数从ebp地址+8的地址中取出(这个地址中存放形参数),并放入到eax寄存
    	addl	$3, %eax        //将3与eax寄存器的内容相加结果保存到eax寄存器中
    	popl	%ebp            //将esp中内容出栈,存放到ebp中,ebp中此时存放f函数堆栈的栈底地址,然后栈顶上移
    	.cfi_restore 5
    	.cfi_def_cfa 4, 4
    	ret
    	.cfi_endproc
    .LFE0:
    	.size	g, .-g
    	.globl	f
    	.type	f, @function
    f:                     //f函数入口地址
    .LFB1:
    	.cfi_startproc
    	pushl	%ebp           //ebp寄存器内容压栈
    	.cfi_def_cfa_offset 8
    	.cfi_offset 5, -8
    	movl	%esp, %ebp     //esp值 赋给ebp,设置f函数的栈基址
    	.cfi_def_cfa_register 5
    	subl	$4, %esp       //esp指针下移
    	movl	8(%ebp), %eax  //eax是累加寄存器,将参数从ebp地址+8的地址中取出(这个地址中存放形参数),并放入到eax寄存器中
    	movl	%eax, (%esp)   //将eax寄存其中存放的数据放到esp地址中
    	call	g              //调用g函数
    	leave                  //相当与movl %ebp %esp, popl ebp,退出当前堆栈,返回上一级函数堆栈
    	.cfi_restore 5
    	.cfi_def_cfa 4, 4
    	ret                    //函数返回,回到上一级调用
    	.cfi_endproc
    .LFE1:
    	.size	f, .-f
    	.globl	main
    	.type	main, @function
    main:
    .LFB2:
    	.cfi_startproc
    	pushl	%ebp      //ebp寄存器内容压栈,保存main函数的上级调用函数的栈基地址
    	.cfi_def_cfa_offset 8
    	.cfi_offset 5, -8
    	movl	%esp, %ebp  //esp值 赋给ebp,设置main函数的栈基址
    	.cfi_def_cfa_register 5
    	subl	$4, %esp    //esp指针下移
    	movl	$8, (%esp)   //将8 存放到esp指针所指向的空间
    	call	f          //调用f函数
    	addl	$1, %eax    //将 f(8)运行结果在eax的值 与 1相加然后哦存放在累加其eax 中
    	leave //相当与movl %ebp %esp, popl ebp,将ebp值赋给esp,pop先前栈内的上级函数栈的基地址给ebp,恢复原栈基址
    
    	.cfi_restore 5
    	.cfi_def_cfa 4, 4
    	ret
    	.cfi_endproc
    .LFE2:
    	.size	main, .-main
    	.ident	"GCC: (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2"
    	.section	.note.GNU-stack,"",@progbits

    分析

            第1行为gcc留下的文件信息;第2行标识下面一段是代码段,第3、4行表示这是g函数的入口,第5行为入口标号;6~20行为 g 函数体,稍后 分析;21行为 f 函数的代码段的大小;22、23行表示这是 f 函数的入口;24行为入口标识,25到41为 f 函数的汇编实现;42行为f函数的代码段的大小;43、44行表示这是main函数的入口;45行为入口标识,46到62为main函数的汇编实现;63行为main函数的代码段的大小;54到67行为 gcc留下的信息。

    汇编中的push和pop:

    pop系列指令的格式是:
    pop destination
    pop指令把栈顶指定长度的数据存放到destination中,并且设置相应的esp的值使它始终指向栈顶位置。

    push刚好相反。

    pushl %eax 等价于 

          subl $4 %esp //栈顶指针下移

          movl %eax (%esp)//将%eax 存放在  esp指针指向的空间

    popl %eax 等价于

           movl (%esp) %eax  //将esp指向空间的内容存放到eap中

           addl %4 %esp  //栈顶指针上移

    LEAVE是释放当前函数或者过程的栈框架,即相当于以下两条指令:
            movl %ebp  %esp
            popl  ebp

    ret指令:

           等于 pop  %ebp   即恢复ebp的值。


    流程图下:

    main的执行过程:


    f的执行过程:


    g的执行过程:


    pop ebp完了之后,返回到上一个堆栈,其余类似。




    单任务计算机是怎样工作的:计算机的最小模型由cpu和内存组成,对于单任务,计算机先将机器码加载入内存,然后控制器将eip所指向的内容取出即取指,然后顺序执行。执行过程中遇到控制指令,可能跳转。在指令执行过程中,如果遇到函数调用,要借助堆栈来实现,先将调用栈基地址压入堆栈,再压入调用函数的返回地址(下一条指令的地址即当前eip中的内容),此时的栈顶esp作为被调用函数栈的栈底ebp1,之后进入被调用函数继续执行。函数返回时栈的操作是相反的过程。通常函数的返回值由eax寄存器来保存。

      多任务计算机是怎样工作的:多任务的顺利工作要借助中断,进程间调度机制时间片轮转方法等来实现多任务在计算机上工作,在多任务计算机工作过程中,由于要在多个任务间进行切换,所以由单任务计算机工作中函数调用得到启发。在各个任务之间进行切换之前,要保存前一个任务的栈顶,eip,标志位等信息,以保证该任务能够恢复。








    展开全文
  • 前言大家好,本篇我们主要分享数据结构中最基础...线性表数据结构:就是数据排列像一线一样的结构,只有前后的方向,连续的内存空间:就是存储数据在内存中的地址是连续的,例如存储一个长度为3的整数int[3]数组,...
  • 目录前言数组链表总结前言大家好,本篇我们主要分享...线性表数据结构:就是数据排列像一线一样的结构,只有前后的方向,连续的内存空间:就是存储数据在内存中的地址是连续的,例如存储一个长度为3的整数int[3]...
  • 广播法则(broadcast)

    2019-11-08 15:49:00
    广播法则(broadcast)是科学运算中经常使用的一个技巧,它在快速执行向量化的同时不会占用额外的内存/显存。 Numpy的广播法则(3)... 当输入数组的某个维度的长度为1时,计算时沿此维度复制扩充成一样的形状 PyT...
  • 本来想换新机的,看了一下自己的花呗,还是算了吧,果然,人在空虚的,更容易瞎花钱,那就把硬盘,内存条...往下拧螺丝,注意拆下来的螺丝放置的位置关系,部分螺丝可能鹤立鸡群,长度不一样 拆完揭开后盖即可 有些乱
  • 网上不是说,如果处理的记录数大于SXSSFWorkbook内存中保留的记录数,不是会把多余的写入到磁盘么,就像那个队列一样,先入先出,当大于队列的长度的时候。如果设置过大,都读到内存中处理会不会导致内存溢出呢 请问...
  • 线性表就是数据排成像一线一样的结构,比如数组、队列、栈。非线性表数据之间并不是前后关系,比如树、堆、图等。 因为数据是相同类型的,内存空间是连续的,使得数组可以随机访问,但也使数组不易插入和删除。 ...
  •  A、可以保存任何类型的数据长度 但整形主键的话必须为整数 只能编写标准sql语句 分页和mysql一样 imit 5 offset 3 limit 3,5 跳过前面3  B、写一个类继承SQLiteOpenHelper 数据库文件所在路径为:应用...
  • 数组结构:就是在内存中开辟一块连续的空间,各个元素排排坐,能超过开辟的空间长度; 链表结构:预先开辟空间,各个元素一定连续挨着,但是每个元素都有它下一个元素的"地址",通过地址找到下一位,这就是(单向)链表,...
  • 数组

    2018-10-02 14:29:00
    线性表:数据排成一线一样的结构。 非线性表: 数据之间不是简单的前后关系。 数组的特性 随机访问。随机访问的缺点:如果要想在数组中删除、插入一个数据,为了保证连续性,就需要做大量的数据搬移工作。 数组...
  • ​ 所谓线性表就是将数据排成像一长线一样的结构,数组、链表、队列、栈都是线性表结构,线性表上的数据最多只有前后两个方向。与这样线性结构相对应的就是非线性结构,比如数组、堆、图等,数据之间并是简单的...
  • CMUSphinx发音词典 这是rust库的一个分支,用于从发音词典中获取发音。... 为了解决显而易见的问题,是的,这比哈希图要慢,因为它内部使用了(就像原始的板箱一样)。 尽管如此,它仍然比基于范围的文件查找或旧的
  • 函数的缓冲级都是指针占用内存 使用动态内存管理的方式 有数据则创建内存放入数据作为一个缓冲级 如果模块应答的数据在规定的时间内没有响应则删除此缓冲级 函数前都有注释介绍 下面介绍一些常用的函数: at_init...
  • 这个可以理解为 TCP的 SND_BUF 和 RCV_BUF,只不过单位不一样 SND/RCV_BUF 单位是字节,这个单位是包。 最大传输单元: 纯算法协议并不负责探测 MTU,默认 mtu是1400字节,可以使用ikcp_setmtu来设置该值。该值将...
  • cpu,内存,io 使用率都高 , 均在10%以下 1. 经常同一时间出现多慢查询 附件: http://pan.baidu.com/s/1hq3tQDY 这个里是当前mysql的运行状态,配置参数和部分慢查询日志 求各位帮个忙, 最近被搞的...
  • # 联合查询操作所能使用的缓冲区大小,和sort_buffer_size一样,该参数对应的分配内存也是每连接独享 thread_cache_size = 8 # 这个值(默认8)表示可以重新利用保存在缓存中线程的数量,当断开连接时如果缓存中还有...
  • 以θ取步长为例,当θ取得步长大的时候,映射的(ρ,θ)对少些,反之则多,但是我们有看到,映射后的点对是需要求交点的,上述画出来的曲线是连续的,然而实际上因为θ步长的存在,他可能是连续的,像上个图一样,...
  • 最上层的业务,每个组件表示一完整的业务线,彼此之间互相独立。 该案例中分为:干活集中营,玩Android,知乎日报,微信新闻,头条新闻,搞笑视频,百度音乐,我的记事本,豆瓣音乐读书电影,游戏组件等等。 ...
  • ②按下三连音按钮,添加三连音,选中音符作为三连音的首个音符,该音符的长度就是三连 音的基准长度; 方法二: ①将光标定位到要添加的位置; ②按下三连音按钮,给光标位置的前一个音符添加三连音; 如果需要...
  • 6.15 我如何声明大小和传入的数组一样的局部数组? 6.16 如何动态分配多维数组? 6.17 有个很好的窍门,如果我这样写:intrealarray[10];int*array=&realarray[-1];我就可以把“array”当作下标从1 开始的数组。...
  • 新的消息总是放在队列的末尾,接收的时候并总是从头来接收,可以从中间来接收。消息队列允许一个或多个进程写消息,一个或多个进程读取消息。Linux维护了一系列消息队列的msgque向量表。其中的每一个单元都指向一...
  • 然后又使用一些内存分配技巧使namestr数组用起来好像有多个元素,namelen记录了元素个数。它是怎样工作的?这样是合法的和可移植的吗? 23  2.8 我听说结构可以赋给变量也可以对函数传入和传出。为什么K&R1却明确...
  • 注:①测试使用小米9手机,单表数据量从最小100到最大200W,字段为30个String+一个自增ID,每个字符串长度都在20到30长度的随机字符,测试过程没有严格做到控制变量法,所以测试并不是很严谨,仅供参考;...
  • MAPGIS地质制图工具

    2013-05-06 16:15:30
    备注:a、MapGis-> Excel输出数据时,点线文件必须处于编辑状态,否则成功,点的样式与MapGis中的一样 b、MapGis-> Excel1输出数据时,只要点文件处于编辑状态下既可以,本功能是按照文字位置排列输出到Excel表格...
  • 11群是四个群中最小的群,其中继计次表位于缓冲区的首位,打完电话后查询内存发现出中继群号在内存中是正确的,取完话单后再查就正确了。 结 论: 话单池的一个备份指针Pool_head_1和中继计次表的头指针重合,...

空空如也

空空如也

1 2 3 4 5
收藏数 86
精华内容 34
关键字:

内存条长度不一样