الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

لماذا لايمكنني إستخدام المؤشرات في الجافا، وهل هناك طريقة ملتوية لحل هذا المشكل؟

بدأه zirek99i في 13 نوفمبر 2013 · 14 رد · 839 مشاهدة · في JavaSE
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم ورحمة الله تعالى وبركاته

 

أنا مبتدئ في الجافا، وأظن أن مشكلتي واضحة من العنوان، فهل هذا هو الـ Standard المستخدم ؟

تم تعديل هذه المشاركة بواسطة zirek99i في 13 نوفمبر 2013 في 01:26

#2

أهلاً أخي :)

بصراحة لا أدري لماذا هناك اعتقاد بأن جافا لا تحوي مؤشّرات .. بالعكس .. جافا لا تحوي غير المؤشرات !

فالمؤشر حسب التعريف هو متغير صحيح يحوي عنواناً .. وهذا بالضبط حال جميع كائنات جافا .. ليست إلا مؤشرات للكائن ..

مثلا في C

string x;

بدون مؤشرات ..

string *x=new string();

مع مؤشرات ...

 

اما جافا فلا تسمح لك إلا بــ :

String x=new String();

يسمّونها مرجعيات Reference ولكنها عين المؤشرات ..

وتمرير الكائنات دائما يكون بالمرجع ( أي مؤشر ) وليس بالقيمة .

الخلاصة : ما الذي يمكنك عمله بالمؤشرات ولا يمكنك عمله داخل جافا ؟ معرفة العنوان الحقيقي للعنصر ؟ أصلاً جافا تستعمل آلة افتراضية :D  ماذا غير ذلك ؟ لا شيء !!

 

(كله رأي شخصي ولم أقرأه في أي مكان .. واحتمال الخطأ كبير )

بما أنك قادم من ++C فما رأيك بالدخول بسرعة خاطفة والاطلاع على مقالات :انتقل من ++C إلى Java بسلاسة  ..

بالتوفيق

2
#3

حسنا، ماتقولونه يبدو منطقياً جداً،

 

صحح لي ما يلي إن كنت مخطئاً، 

عندما أقوم بتعريف Instance جديدة لـ Class بالشكل التالي:

String str = new String ();

فإن ماأقوم به، هو إنشاء Reference بالـ Stack الى String التي يتم حجز مساحتها في الـ Heap، ما يعني أن الكود السابق هو مرادف لآستخدام malloc في السي أو new  في السي++، أو HeapAlloc بالويندوز .. الخ، المهم، أنني على سرت على نفس المنطق وقمت بإنشاء الكود التالي:

class Zirek99i
{
	private int id;
	private String name;
	
	public Zirek99i (int id, String name)
	{
		this.id = id;
		this.name = name;
	}

	public String toString() {
		return "Zirek99i [id=" + id + ", name=" + name + "]";
	}

	public void setId(int id) {
		this.id = id;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	
}

public class Test {

	public static void main(String[] args) {
		
		Zirek99i zr = new Zirek99i(18, "OUSSAMA");
		System.out.println(zr.toString());
		
		/* this passed the value reside on the stack */
		printInfo(zr);
		System.out.println(zr.toString());

	}
	
	static void printInfo(Zirek99i zr)
	{
		
		/* This print the info from the heap */
		System.out.println(zr.toString());
		zr.setId(99);
		
		/* The original data is damaged LOST - */
		System.out.println(zr.toString());
		System.out.println();
	}
	

}

لكن الداتا قد تعرضت للتلف أثناء إستخدام الدالة، المهم قمت بتعديل طفيف على الـ Methods وإضافة الـ Getters و Setters كما بالكود التالي:

static void printInfo(Zirek99i zr)
	{
		
		Zirek99i ri = new Zirek99i (zr.getId(), zr.getName());
		System.out.println(ri.toString());
		
		ri.setId(888);
		ri.setName("ZIREK99I");
		System.out.println(ri.toString());
	}

حسنا، لقد قمت بحل المشكل لكنني غير راض عن هذا الحل، لأنه غش، المهم إستخدمت نفس الطريقة، مع الكلاس التي أريد إستخدامها، والتي من أجلها طرحت السؤال بالمنتدى،

public class Test {

	public static void main(String[] args) {
		
		LinkedList<String> Active_hosts = new LinkedList<>();
		Active_hosts.add("host_1");
		Active_hosts.add("host_2");
		Active_hosts.add("host_3");
		Active_hosts.add("host_4");
		Active_hosts.add("host_5");
		
		printAvailableHosts(Active_hosts);
	}
	
	static void printInfo(LinkedList<String> ls)
	{
		/*
		 * how can i filter, remove unwanted items using this methods
		 * without affecting the original one?
		 * 
		 * i guess that it must be a simple way, to accomplish my gool, like just passing by 
		 * value something may look like: static void printInfo(new LinkedList<String> ls)
		 * 
		 * but it did not compile
		 */
		
		/*
		 * this is my attempt:
		 */
		// .. not working deleted !! 
		
		return;
	}

	static void printAvailableHosts(LinkedList<String> ls)
	{
		System.out.println("number of available hosts: " + ls.size());
		
		for (Iterator<String> iterator = ls.iterator(); iterator.hasNext();) {
			String string = (String) iterator.next();
			System.out.println(string);
		}
		
		return;
	}
}

لكن، صراحة لم أعلم من أين أبدأ؟

تم تعديل هذه المشاركة بواسطة zirek99i في 13 نوفمبر 2013 في 03:25

#4

السلام عليكم

 

بداية, أحب أن أؤكد أنه لا وجود للمؤشرات في الجافا :)

 

@مصطفى

باستخدام الـ references في الجافا, هل يمكنك كتابة كود مُكافئ لـ :

char * string_in() {
    char c = 0;
    char * tmp = NULL;
    int length = 100;
    int size = -1;
    tmp = (char *) calloc(sizeof (char), length);

    while ((c = getchar()) != '\n') {
        if (size++ == length) length += length, tmp = (char *) realloc(tmp, sizeof (char) * length);
        if (tmp == NULL) {
            printf("No free memory for string!\n");
            exit(-1);
        }

        tmp[size] = c;
    }

    tmp = (char *) realloc(tmp, sizeof (char) * size);
    if (tmp == NULL) {
        printf("No free memory for string!\n");
        exit(-1);
    }

    return tmp;
}

أو كود آخر يُكافئ هذا مثلا :

bool is_number(const char* number_string) {
    int counter = 0;
    while (*number_string) {
        char c = *number_string++;
        if (c == '.') {
            counter++;
            if (counter > 1)
                return false;
        } else {
            if (!std::isdigit(c))
                return false;
        }
    }
    return true;
}

هناك بعض الملاحظات المهمة بخصوص الـ references في الجافا :

  1. الـ references في الجافا لا يُمكن أن تُشير إلى مكان incorrect من الذاكرة (إما أن تُشير إلى مكان valid أو تحمل القيمة NULL).
  2. لا يوجد الـ arithmetical في مؤشرات جافا.
  3. يتم تسيير الذاكرة بشكل تلقائي عن طريق الـ GC.
  4. هناك فرق شاسع بين الـ references الموجودة في سي++ و تلك الموجودة في جافا.

 

بما أنك جديد على الجافا و قادم من السي++, أنصحك بقراءة هذا الكتاب, سيفيدك جداً.

 

 

تحياتي.

تم تعديل هذه المشاركة بواسطة Snack3r في 13 نوفمبر 2013 في 03:51

1
#5

لقد حللت المشكل ;) ، وفهمت طريقة عمل الـ references في الجافا، شكرا لك Snacker و مصطفي

import java.util.Iterator;
import java.util.LinkedList;

public class Test {

	public static void main(String[] args) {

		LinkedList<String> Active_hosts = new LinkedList<>();
		Active_hosts.add("host_1");
		Active_hosts.add("host_2");
		Active_hosts.add("host_3");
		Active_hosts.add("host_4");
		Active_hosts.add("host_5");

		System.out.println("the original");
		printAvailableHosts(Active_hosts);
		
		editInfo(Active_hosts);
		System.out.println("in the end");
		printAvailableHosts(Active_hosts);
	}

	static void editInfo(LinkedList<String> ls) {
		LinkedList<String> k = new LinkedList<String>(ls);
		
		System.out.println("Inside the editInfo methods");
		printAvailableHosts(k);
		
		k.removeFirst();
		k.removeLast();
		printAvailableHosts(k);
		
		System.out.println();
		return;
	}

	static void printAvailableHosts(LinkedList<String> ls) {
		System.out.println("number of available hosts: " + ls.size());

		for (Iterator<String> iterator = ls.iterator(); iterator.hasNext();) {
			String string = (String) iterator.next();
			System.out.println(string);
		}

		return;
	}
}
#6

يبدو أنك حللت المشكلة فقط بالنسبة لحالة LinkedList أو الكائنات التي تأخذ وسيطا في بانيها كائناً آخر ..

ولكن سأتابع ردي الذي كتبته

اقتباس

صحح لي ما يلي إن كنت مخطئاً، 

عندما أقوم بتعريف Instance جديدة لـ Class بالشكل التالي:

String str = new String (); فإن ماأقوم به، هو إنشاء Reference بالـ Stack الى String التي يتم حجز مساحتها في الـ Heap، ما يعني أن الكود السابق هو مرادف لآستخدام malloc في السي أو new  في السي++، أو HeapAlloc بالويندوز .. الخ

كلام صحيح ويدل على فهمك للأمر ..

يوماً عن يوم .. أزداد ثقة بأن جافا تتعامل مع المؤشرات
__________________________________________
بالنسبة لسؤالك أخي Zi فهو يؤكّد كلامي .. لا يوجد في جافا تمرير للكائنات إلا بالمرجع أي كأنك تستند مؤشرات لبعضها ..
وبالنسبة لسؤالك

        /*
         * how can i filter, remove unwanted item for this methods
         * without affecting the original one?
         *
         * i guess that it must a simple method, like just passing by
         * value solething like: static void printInfo(new LinkedList<String> ls)
         *
         * but it did not compile
         */
        
        /*
         * this is my attempt:
         */
ببساطة ليس هناك سوى طريقة واحدة .. تنشئ كائن جديد وتنسخ محتويات الكائن القديم عنصراً عنصراً .. إذا كنت تريد التعديل ..فلا بد أن تنسخ المحتويات .. مرة أخرى
ولكن بالنسبة للأنواع الأساسية فهي تمرر بالقيمة فيمكنك اسنادها بشكل طبيعي ..
الكود التالي يوضّح مشكلتك , كما أنه يؤكّد على أن الدوال تعيد عنوان الكائن وليس نسخة من الكائن
import java.util.ArrayList;

public class pointers2 {    
    public static void main(String[] args) {
        ArrayList <test>array1=new ArrayList<test>();
        ArrayList <test>array2=new ArrayList<test>();
        
        for(int i=0;i<10;i++)
            {
                array1.add(new test(i,'b'));
                array2.add(array1.get(i));
            }
        array2.get(5).set(-5, 'a');
        
        for(int i=0;i<10;i++)    
            {
                System.out.print( array1.get(i).a+"   "+array2.get(i).a);
                System.out.println("   "+ array1.get(i).b+"   "+array2.get(i).b);
            }
    }
}
class test{
    int a;
    char b;
    test(int a,char b)
    {
        this.a=a;
        this.b=b;
    }
    void set(int a,char b)
    {
        this.a=a;
        this.b=b;
    }
}
ولا أعرف يوجود حل آخر غير إنشاء نسخ المتغيرات واحدا واحداً . .. ولكن الأمر ليس بهذه الصعوبة ..استخدم الدالة clone
كما يلي :
array2.add(array1.get(i).clone());
يمكنك القيام بـoverride للدالة فهي من Object كما يمكنك كتابة واحدة خاصة بك ..وتذكّر أن تنسخ جميع العناصر الداخلية باستخدام clone إن لم تكن أنواعاً رئيسية (أولية)
في المثال السابق كتبت الدالة بنفسي بدون override
__________________________________________
@Snack3er
انظر إلى الكود الأول الذي أرفقته .. فقط استبدلت كل * بــ [] حيث أن جافا تعتبر المصفوفات كائنات فهي بالتالي كما أكدت مراراً  مؤشرات
وإليك الإثبات : هذا هو الكود الأول
    char [] string_in() {
        char c = 0;
        char [] tmp = NULL;
        int
    length = 100;
        int size = -1;
        tmp =(char[])calloc(sizeof ("char"), length);

        while ((c = getchar()) != '\n') {
            if (size++ == length){ length += length; tmp = (char[])realloc(tmp, sizeof ("char") * length);}
            if (tmp == NULL) {
                printf("No free memory for string!\n");
                exit(-1);
            }

            tmp[size] = c;
        }

        tmp = (char[])realloc(tmp, sizeof ("char") * size);
        if (tmp == NULL) {
            printf("No free memory for string!\n");
            exit(-1);
        }

        return tmp;
    }
طبعاً حتى يعمل قمت بتنفيذ الدوال السابقة من باب الإثبات أكثر وهذه هي أجسام الدوال :
private static final char[] NULL = null;    private char[] calloc(int sizeof, int length) {        return new char[sizeof*length];    }    private char[] realloc(char[] tmp, int i) {        char[]T=new char;        for(int j=0;j<i;j++)            T[j]=tmp;        return T;    }    private int sizeof(String string) {        if(string.contains("char"))            return 2;//char is 2 bytes in java        if(string.contains("short"))            return 2;        if(string.contains("double"))            return 8;        return 4;    }    private void exit(int i) {        System.exit(i);    }    private void printf(String string) {        System.out.println(string);    }    private char getchar() {        try {            return (char)System.in.read();        } catch (Exception e) {            // TODO Auto-generated catch block            e.printStackTrace();            return 0;        }    }
ما رأيك الآن :) ... أعرف أن جميع المراجع لا تذكر ذلك .. ولكن هذه هي الحقيقة ..
بالنسبة للكود الثاني فلا يمكن تمثيله لأنه يعتمد على زيادة العنوان في الذاكرة وهذا هو الاختلاف  الوحيد كما ذكرت في ردّي ..بين مؤشرات C ومؤشرات جافا .. وأي عملية أخرى على المؤشرات يمكن لجافا القيام بها على الكائنات
اقتباس
يتم تسيير الذاكرة بشكل تلقائي عن طريق الـ GC.

لا أظن أن هذا اختلاف بين المؤشرات تحديداً , فهو قضية عامّة
اقتباس
هناك فرق شاسع بين الـ references الموجودة في سي++ و تلك الموجودة في جافا.

لا أتحدث عن references الــ++C بل عن الــ pointers ..
ويمكنني القول بثقة أن references الــ Java هي ذاتها pointers ++C مع الاختلاف الوحيد في عدم سماح Java بالتعامل مع عناوين الذاكرة بشكل مباشر .. مثل زيادة العنوان الذي يشير له المؤشر أو إسناد قيمة عنوان بشكل مباشر .. حيث تسمح جافا باستخدام new فقط لإسناد المؤشرات  أو بإسناد مؤشر لمؤشر سابق ..
كما قلت .. الكلام السابق استنتاجي بحت من الفروق بين اللغتين .. ولا أظن أن أحد المراجع سيتجرّأ على قول مثله :D

والله ولي التوفيق
1
#7
ويمكنني القول بثقة أن references الــ Java هي ذاتها pointers ++C مع الاختلاف الوحيد في عدم سماح Java بالتعامل مع عناوين الذاكرة بشكل مباشر .. مثل زيادة العنوان الذي يشير له المؤشر أو إسناد قيمة عنوان بشكل مباشر .. حيث تسمح جافا باستخدام new فقط لإسناد المؤشرات  أو بإسناد مؤشر لمؤشر سابق ..
كما قلت .. الكلام السابق استنتاجي بحت من الفروق بين اللغتين .. ولا أظن أن أحد المراجع سيتجرّأ على قول مثله :D

إلا أن الـ Pointers في السي++، يمكن التحكم فيها بشكل عادي تماماً،

صراحة كنت أنثظر ردك، حول هذه المسألة، فأنا أحتاج أن أفهم بالضبط كيف تعمل الجافا من الداخل، وليس تلك الكلمات السطحية من الكتب، 

المهم بحتث كثيرا في اليومين الماضيين حول هذه المسألة، وأفضل جواب وجدته،

http://stackoverflow.com/questions/869033/how-do-i-copy-an-object-in-java

وهو تقريباً نفس ماطرحته في مشاركتك.

أنا الآن أبحث عن طريقة، تمكنني من كتابة، method بالشكل التالي

static void my_CopyObject(_in_ objectToCopyTo, _in_ ObjectToCopy, _opt_ sizeof(ObjectToCopy))
	{
		// reserve memory for objectToCopyTo
		// copy objectToCopy in objectToCopyTo
	}

لكنني أشعر كمن يبحث في لاشئ، فما أسهل كتابة مثل هذه الدالة بالسي أو الأسمبلي لكن بالجافا .......؟؟؟؟

#8

يقول John Wiley  في كتابة Assembly language step by step  : كلما سألت أحداً ولم يكن يعرف الإجابة يقول بكل برود : " ولم قد ترغب بفعل ذلك ؟" ..

وأقولها لك : لماذا قد ترغب بفعل ذلك ؟ الجافا لها طريقة تفكير مختلفة والدالة clone موجودة في الفئة Object ومعظم الفئات قامت بعمل Override لها .. وهي تقوم بما تريد القيام به بالتحديد ..

بصراحة أشعر أن جافا تقول لنا دائما : "استعمل .. لا تخترع " على عكس C ..

اقرأ عن الدالة clone

 

بالتوفيق

#9
اقتباس

انظر إلى الكود الأول الذي أرفقته .. فقط استبدلت كل * بــ [] حيث أن جافا تعتبر المصفوفات كائنات فهي بالتالي كما أكدت مراراً  مؤشرات

وإليك الإثبات : هذا هو الكود الأول

 

يا حبيبي :P

و هل تظن أن كود realloc يعمل بالسهولة التي كتبتَ أنت بالجافا ؟ :huh:

realloc تقوم بتوسيع حجم الذاكرة التي يُشير إليها المؤشر المُمرر كوسيط بينما أنت تقوم بحجز ذاكرة جديدة مختلفة تماماً ..

 

.. إذا قلت لك اكتب لي كود يُكافئ هذا :

void realloc_s(char **p, size_t size)
 {
    char *d = (char*) realloc(*p, size * sizeof (char));
    if (d) *p = d;
    else {
        puts("no memory !!");
        free(*p);
        *p = 0;
        exit(EXIT_FAILURE);
    }
    return;
}

ماذا ستفعل ؟ :D

تم تعديل هذه المشاركة بواسطة Snack3r في 13 نوفمبر 2013 في 19:27

#10

:D

قلت أن الفرق الوحيد هو صلاحية الوصول لعناوين الذاكرة الفعلية .. فجافا (من باب الأمان ربما) تبقي المطوّر بعيداً عن الذاكرة الفعلية وتعيشه في عناوين وهمية ..

يعني إذا كان لا يهمّك فعليّاً ماذا حدث .. فلن تشعر بأي فرق ..

بالنسبة للكود الذي طلبته :

يمكن كتابته هكذا :

void realloc_s(char [][]p, int size)
     {
        try{
            char []d = (char[]) realloc(p[0], size * sizeof ("char"));
        }
        catch(OutOfMemoryError x)
            puts("no memory !!");
            //free(*p);//TO GC
            p[0] = null;
            exit(EXIT_FAILURE);
        }
        p[0] = d;
        return;
    }

والفرق هو إصدار استثناء عوضاً عن الإسناد إلى NULL  وهذا تابع للدالة realloc وليس للمؤشرات !

هل هناك فرق آخر ؟ غير صلاحية الوصول لعناوين الذاكرة :)

#11

يا مصطفى حرام عليك :D

هل قمت بتجربة الكود الذي قمت بكتابته ؟ أنا جربته و طلع لي exception مكتوب فيه "مفيش يا عم مؤشرات في الجافا" :P

 

لدي بعض الملاحظات على كودك علما أنه لا يؤدي المطلوب (حتى اللحظة) :

  1. إذا كنا نتحدث عن المؤشرات فمن أهم مميزاتها حجز و تحرير الذاكرة يدوياً, طبعا هذا الأمر له مخاطره لكنه أيضا يُعطي الحرية المطلقة للمبرمج في التعامل مع الذاكرة و هو الشيء الغير موجود في الجافا. (لا تحاول إيجاده لأنه أصلا يتعارض مع مفهوم الـ GC :rolleyes: )
  2. توسيع الذاكرة المحجوزة يختلف تماماً عن حجز ذاكرة جديدة بحجم أكبر.
  3. المرونة التي توفرها مصفوفة المؤشرات تختلف عن تلك التي نجدها في مصفوفة char و أنت تعلم ذلك جيدا ..

 

 

 

 

تحياتي.

#12

هل تناقشني في الموضوع بشكل جدّي إلى هذه الدرجة  :D

الفكرة لا تحتاج كل هذا النقاش .. طبعاً مؤشرات C غير قابلة للمناقشة بمرونتها وفعاليتها ..

ولكن كل ما كنت أقوله أن جافا تتيح التعامل مع الكائنات تماماً كما تتيح ++C التعامل مع مؤشرات إلى كائنات .. وتمنعك من الوصول لعناوين الذاكرة الحقيقية وكل ملحقاتها ..

لا أناقش أن جافا توفر مؤشرات C ولكنها تستعمل نفس المفهوم في التعامل مع الكائنات ... وبس :)

 

اقتباس
أنا جربته و طلع لي exception مكتوب فيه "مفيش يا عم مؤشرات في الجافا"

جميل .. بيئة تطوير عربية :D

أعتذر لإطالة النقاش .. ولكن بصراحة كنت كاتب مقال صغير عن " المؤشرات في جافا" وخفت من نشره لأنه يُمثّل وجهة نظري فقط .. يعني كان الجافاويين رح ياكلوني بلا ملح :)

على العموم هذه هي المقالة في المرفقات .. حتى أريح ضميري :]

 

شكراً لتحمّلي ;)

java pointers.txt

#13
اقتباس

كنت كاتب مقال صغير عن " المؤشرات في جافا" وخفت من نشره لأنه يُمثّل وجهة نظري فقط .. يعني كان الجافاويين رح ياكلوني بلا ملح :) على العموم هذه هي المقالة في المرفقات .. حتى أريح ضميري

 

اكتبها في موضوع منفصل, تبدو جميلة :)

مع أنني لم أتمكن من قراءتها لأن النوتباد عندي ما يقرأ الملفات العربية :mellow:

#14

لقد قرأت المقالة وهي رائعة جداً، شكراً لك،

سيكون من الجيد لو قمت بإضافة صور لتنقيح الأكواد التي كتبتها، حتى تنبذ أي رأي مخالف وتنشر الموضوع بالمنتدى،

 

والله ولي التوفيق

#15

شكرا لكما .. على كل حال ربّنا يستر .. هذه هي المقالة .

بالتوفيق

مواضيع مشابهة

عدد الزوار حالياً

المتواجدون خلال آخر دقيقتين · يتحدّث كل ٣٠ ثانية

—الإجمالي—أعضاء مسجّلون—زوار بدون تسجيل

جارٍ التحقق من المتواجدين…