× מי אנחנו? התוכנית הראשונה משתנים קלט ופלט אופרטורים חשבוניים משפטי בקרה לולאות לולאות דו ממדיות casting sizeof typedef פונקציות רקורסיות מצביעים מצביע כפול מערכים מערכים דינאמיים מערכים דו ממדים מחרוזות חיפוש בינארי מיון בועות מיון בחירה מיון הכנסה מיון מהיר מיון מיזוג

Let's learn C

מצביעים

למה צריך מצביע?

נגיד שאנחנו רוצים לבנות פונקציה שמשנה את הערכים של כמה משתנים שנשלחים לפונקציה.
עד עכשיו לא היה באפשרותנו לעשות זאת, לאחר שנלמד על מצביעים נוכל.

מה זה מצביע?

מצביע הינו משתנה אשר מכיל כתובת של משתנים אחרים.

יצירת משתנה מסוג מצביע:

אנו יוצרים משתנה מסוג מצביע בצורה הבאה:
Type *name;

סימון:

דוגמה 1 – הדפסה של מקום בזיכרון:

CODE 1:
#include <stdio.h>
void main()
{
  int number = 8;
  printf("%d",&number);
}

בשורה זאת תתבצע הדפסה אבל לא של המשתנה number אלה את הכתובת שלו בגלל הסימון & , אם לדוגמה המשתנה number נמצא בכתובת 1000 אז שורת הקוד הזאת תדפיס לנו 1000. בלי הסימן & היינו מדפיסים את הערך בתוך המשתנה.

דוגמה 2 – קוד עם מצביעים:

CODE 2:
#include <stdio.h>
void main()
{
  int number = 10;
  int *ptr = &number;
  *ptr = *ptr + 5;
  printf("%d",ptr);
}

בשורה הראשונה יצרנו משתנה מסוג int שערכו הוא 10.
בשורה השנייה יצרנו מצביע מסוג int ונתנו לו את הכתובת של number.
בשורה השלישית אנחנו מגדילים את הערך שנמצא ב number ב5 בעזרת המצביע (בגלל ש-ptr מצביע על הכתובת של number יש לנו שליטה על הערך שנמצא בתוכו).
כאשר אנחנו רושמים *ptr אנחנו מתייחסים לערך שנמצע בתוך number כאשר אנחנו רושמים ptr בלי * אנחנו מתייחסים לכתובת של המשתנה עליו אנחנו מצביעים.
לכן בשורה הרביעית אנחנו מדפיסים את הכתובת של number ואין צורך ב & כי מצביע נחשב לכתובת , הוא שקול ל-&number.

שליחת ערכים לפונקציה by value:

עד עכשיו כאשר שלחנו ערכים לתוך פונקציה שלחנו בשיטה שנקראת by value .
שיטה זו אומרת שאנו לא שולחים את המשתנה עצמו אלא העתק מדויק (מבחינת ערכים) של המשתנה שלנו, וכל שינוי שביצענו על המשתנה לא נשמר בפונקציית הmain (כלומר, התבצע שינוי זמני בלבד).

דוגמה לשליחה by value :
בשליחה בצורה הזו, הערכים שלנו ישארו 11 ו31 ולא ישתנו מחוץ לפונקציה.

CODE 3:
#include <stdio.h>
void inc_by_one(int num_a , int num_b);
void main()
{
  int num_a = 11;
  int num_b = 31;
  inc_by_one(num_a,num_b);
}
void inc_by_one(int num_a , int num_b)
{
  num_a++;
  num_b++;
}

שליחת ערכים לפונקציה by reference:

כדי להתגבר על הבעיה הזו ישנו פתרון של שליחה שנקראת by reference .
בשליחה מסוג זה, אנו שולחים את הכתובת בזיכרון של המשתנה שלנו, וכל שינוי שנעשה בתוך הפונקציה על המשתנה שלנו נשמר (כי לא שלחנו העתק).
חשוב לציין – יש שקוראים לשליחת ערכים מהסוג הזה "העברה" , וזאת משום שאנו מעבירים את המשתנה האמיתי ולא העתק שלו.

דוגמה לשליחה by reference :

CODE 4:
#include <stdio.h>
void inc_by_one(int *num_a , int *num_b);
void main()
{
  int num_a = 11;
  int num_b = 31;
  inc_by_one(&num_a,&num_b);
}
void inc_by_one(int *num_a , int *num_b)
{
  *num_a++;
  *num_b++;
}

ברירת מחדל של מצביע

אם אנחנו ננסה לבצע פעולה כלשהיא על מצביע שלא ביצענו עליו פעולת השמה לכתובת של משתנה אנחנו נקבל שגיאה ולכן חשוב לעשות השמה של מצביע.

גודל של מצביע

כל מצביע לא משנה מאיזה סוג יהיה בעל גודל של 4 בתים.


דוגמה 5 – השמה לא תקינה של מצביע:
אנו נקבל שגיאה בשורה 5 בגלל שאנו מנסים להכניס ערך לכתובת שלא קיימת. התיקון יעשה ע"י לקיחת המשתנה ptr לפני שמתבצעת השורה הבעייתית ולהצביע איתו על משתנה בעל ערך קיים.

CODE 5:
#include <stdio.h>
void main()
{
  int *ptr;
  *ptr = 1000000;
}

דוגמה 6 - תיקון לדוגמה הקודמת:
פה קודם כל הצבענו על number ולאחר מכן שינינו את ערכו ל1,000,000 בעזרת המצביע שלנו.

CODE 6:
#include <stdio.h>
void main()
{
  int number = 6;
  int *ptr = &number;
  *ptr = 1000000;
}

דוגמה 7 – פעולות עם מצביעים (דוגמה לעבודה תקינה ולא תקינה עם מצביעים):

CODE 7:
#include <stdio.h>
void main()
{
  int *p1 , *p2 , number = 10;

  p1 = &number; //p1 points to the variable "number"

  p2 = p1; 	//p2 points to the same address as p1

  *p2 = *p2 – 10; //by using * we refer to the value inside the address that p2 is pointing to.
  therefore the equation is equal to "number = number - 10"
    
  p2 = p2 - 10; //by using the pointer without * we refer to the address in p2.
  //for example if the address is 1000, after this line it will be 990.
  //*you should never use math operators on an address recklessly.

  number = *p1; //we take the value that p1 points to and store it inside the variable "number".

  *p1 = number;//we take the value that is inside "number" and store it inside the variable that
  p1 points to.

  number = p2;//this line of code will work.
  /* NOTE: We take the hex-decimal value of our address, cast it into an integer and than place it
  inside "number".
  it means that we don't know what is the value and we won't be able to use it properly. */	

  p1 = *p2;//this line of code will work.
  /* NOTE: we take a decimal value of the variable that p2 points to and place it inside p1
  (p1 knows to work with hex-decimal addresses only).
  So once again we don't know if our line of code will work properly 
  and we won't be able to use it. */
}