» »

[C] Kazalčna aritmetika

[C] Kazalčna aritmetika

marjan_h ::

Če definiram tabelo kazalcev na char:

char *tabela[] = {"Nekaj", "Drugega"};


In če izpišem tole:
*tabela

Zakaj mi izpiše: "Nekaj" in ne naslova na katerem se nahaja "Nekaj"?

Unilseptij ::

Zelo na hitro iz glave.... poskusi tako, da tabelo definiras brez kazalca... samo kot char tabela[]. Potem izpisi kazalec *tabela in dobil bos zeljeno.

Edit: ali pa v definiciji podaj dejanske naslove do "Nekaj" itd.

Zgodovina sprememb…

WhiteAngel ::

char* tabela[] je polje kazalcev na stringe. Ko dereferencira *tabela, vzame kazalec, ki je tam shranjen brez offseta (isto kot tabela[0]). Izpisuje pa kot C-jevski niz, se pravi, dokler ne pride do \0

Kaj bi pa po tvojem moral izpisati?

Aja, ce zelis naslov, izpisi le tabela. Ali pa &tabela[0].

Zgodovina sprememb…

blay44 ::

Sicer je že beli Angel napisal....
Če je *neKaj, ti pokaže "tisto" kar je na tem naslovu.
Če pa je &neKaj, ti vrne številko naslova na katerem je tiSto.
Tko, po kmečk.

0bv10uStr0ll ::

odgovor je:
printf("%p", tabela);

Dobiš naslov začetka polja (array-a).

tvoj zapis je ekvivalenten temu: char ** tabela;

Torej kazalec na kazalec.

marjan_h ::

WhiteAngel je izjavil:

char* tabela[] je polje kazalcev na stringe. Ko dereferencira *tabela, vzame kazalec, ki je tam shranjen brez offseta (isto kot tabela[0]). Izpisuje pa kot C-jevski niz, se pravi, dokler ne pride do \0

Kaj bi pa po tvojem moral izpisati?

Aja, ce zelis naslov, izpisi le tabela. Ali pa &tabela[0].


Napisal si: je polje kazalcev na stringe. Torej kazalci na stringe in ne stringi. Zakaj mi potem ne izpiše kazalec na string, ampak string?

WhiteAngel ::

marjan_h je izjavil:

Napisal si: je polje kazalcev na stringe. Torej kazalci na stringe in ne stringi. Zakaj mi potem ne izpiše kazalec na string, ampak string?


Kaksno masko uporabljas v printf? Verjetno %s? Ce ja, potem printf interpretira, da kazalec kaze na niz in izpisuje niz. Uporabi %p, kot je 0bv10uStr0ll napisal zgoraj. Verjetno tudi %d deluje kot decimalno stevilo, ampak ga verjetno interpretira kot predznaceno stevilo in bo narobe.

kow ::

Malo sem ze lesen s c-jem, bom poskusil:

1. char *tabela[] =

"tabela" oznacuje pomnilniski naslov, kjer se nahajajo(zaporedno) kazalci na pomnilniski naslov, ki ga bomo interpretirali kot znake oz. characterje (pogovorno: "string")

2)*tabela

Zvezdica oz asterisk je simbol za dereferenciranje - ne vrni mi vrednost na naslovu "tabela", ampak interpretiraj to vrednost kot novi naslov ter mi vrni njegovo vrednost

3) tabela
Vrni mi vsebino naslova "tabela" (ki je v tvojem primeru naslov prvega znaka besede "Nekaj", to je "N").

4) *(tabela + 1) - dereferenciraj drugi pomnilniski naslov, ki kaze na zacetek niza znakov, torej crko "D" od "Drugega"

Zgodovina sprememb…

  • spremenil: kow ()

blay44 ::

Uh dobr, da mamo moderatorje. C++ je kriv.

Probaj tako:
main()

{

char taBela[10] = "abc";

char *kaZalec = &taBela[0];

printf("integer naslov pomnilnika je %d\n", kaZalec);

}

marjan_h ::

WhiteAngel je izjavil:

marjan_h je izjavil:

Napisal si: je polje kazalcev na stringe. Torej kazalci na stringe in ne stringi. Zakaj mi potem ne izpiše kazalec na string, ampak string?


Kaksno masko uporabljas v printf? Verjetno %s? Ce ja, potem printf interpretira, da kazalec kaze na niz in izpisuje niz. Uporabi %p, kot je 0bv10uStr0ll napisal zgoraj. Verjetno tudi %d deluje kot decimalno stevilo, ampak ga verjetno interpretira kot predznaceno stevilo in bo narobe.


Jaz sem uporabil:

puts(*tabela)



1. char *tabela[] =

"tabela" oznacuje pomnilniski naslov, kjer se nahajajo(zaporedno) kazalci na pomnilniski naslov, ki ga bomo interpretirali kot znake oz. characterje (pogovorno: "string")


Vidiš to mi ni jasno. Imamo tabela, ki je pomnilniški naslov. Znotraj tabele so pa kazalci, torej pomnilniški naslovi kjer se nahajajo stringi. Po tej logiki, bi moral ko kličem "puts(tabela)" izpisati pomnilniški naslov, kjer se nahaja tabela. Nato če derefenciram "puts(*tabela)" bi moral izpisati pomnilniški naslov, kjer se nahaja prvi element (string). Če derefenciram tako: "puts(*(tabela+1))" bi moral izpisati pomnilniški naslov, kjer se nahaja drugi element. In če dvakrat derefenciram "puts(**tabela)" pa bi dobili ta niz.

Saj načeloma npr. v službi je važno da dela. Ampak, je pa dobro da tudi razumeš zakaj tako dela.

kow ::

Kako puts() interpretira vrednost, ki ji jo podas - je stvar funkcije.

Poglej si:

http://www.cplusplus.com/reference/cstd...

marjan_h ::

Kaj če uporabimo:

std::cout << *tabela << std::endl;


Kar je sicer c++, vendar je kompatibilno za nazaj. Bolj me zanima če je vam moje razmišljanje logično oz. kaj pravzaprav napačno razumem. Torej če ne uporabimo funkcije za izpis, ampak cout kaj je potem tukaj narobe?

galu ::

Razlog je sila preprost.

printf/cout izpisuje tako, da mu podaš naslov začetka niza. Potem pa on bere/izpisuje tako dolgo, dokler ne naleti na null terminator (\0).

Če želiš dobiti naslov, moraš obiti to, recimo:
std::cout << (int*)*tabela << std::endl;
std::cout << (unsigned long long)*tabela << std::endl;
Tako to gre.

smacker ::

Funkcije so namenjene za izpis stringov, zato "olajšajo" delo programerju in namesto naslova izpišejo string, ki se nahaja na tem naslovu. Se pravi tip char* prepoznajo kot C-string in zato izpišejo niz, namesto dejanske pomnilniške lokacije. Zelo redko želimo res izpisat naslov pomnilniškega mesta. Te vrednosti potrebuješ kvečjemu za debugiranje. Pravilno razumeš, kaj se v teh spremenljivkah v resnici skriva, le funkcije ti tega ne izpišejo. @galu je podal rešitev, kjer char* castaš v int*, ampak bolj pravilno bi bilo v void*, torej kazalec na pomnilniško mesto neznanega tipa (potem je naloga programerja, da poskrbi za pravilno interpretacijo podatkov na tem naslovu). Nekaj na to temo: https://stackoverflow.com/questions/178...
PS: tud
cout<<
je neke vrste "funkcija".
<<
je operator, ki prejme argumente (cout in niz), nekaj naredi in vrača rezultat. Se pravi deluje podobno kot funkcija, le sintaksa klica funkcije in operatorja je nekoliko drugačna.

kow ::

@marjan_h

Mal si hecen - prvo razlagas, da zelis razumeti, potem pa spremenis funkcijo, ki jo uporabljas. In vzames tako, ki uporablja "operator overloading", da bos ja se tezje razumel. Pusti ti zaenkrat c++, ker je trenutno za tvoj nivo znanja prezahteven - pa brez zamere.

Sicer, ti je pa bistvo ze pravilno razlozil smacker -> funkcije so namenjene za izpis "stringov". Da funkcija izpise string, pa potrebuje zaceten naslov. Nato izpisuje dokler ne naleti na bajt oz. pomnilnisko lokacijo z samimi niclami (t.i. "null terminator"), ki se intepretira kot konec stringa.

Kolikor sedaj razumem, ne razumes tocno kako C funkcije delujejo in je to vzrok za nesporazum.

kuall ::

Sem naredil en testni programček. Je vse precej jasno.

Lahko uporabiš tole za izpis naslova prvega člena:
printf("%#010x\n", (unsigned long)*tabela);

puts () pa pričakuje argument tipa string, ne številke, kar bi ti rad in kar je pointer.

Mogoče marjanh prihaja iz javascript okolja, kjer jezik sam ugotovi tip spremenljivke glede na vrednost. Naprimer:

var v = 1; -- Ga pretvori v tip integer.
v = "s"; -- javascript ga avtomatično spremeni v tip string
Medtem ko v C to ne moreš narediti. Compiler se bo pritoževal.

// Pointers.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"


int main()
{
	char *tabela[] = { "Nekaj", "Drugega" };
	puts(*tabela); // Nekaj
	printf("%s\n", *tabela); // Nekaj
	printf("%s\n", *(tabela+1)); // Drugega
	printf("%c\n", **tabela); // N
	printf("%c\n", **(tabela+1)); // D
	///printf("%c\n", ***tabela); // Tu te bo že compiler opozoril, da **tabela ni več pointer in da tega ne moreš narediti.
	printf("%d\n", (unsigned long)*tabela); // 11496240 - desetiški izpis, v naslednji vrstici spodaj pa šestnajstiški
	printf("%#010x\n", (unsigned long)*tabela); // 0x00af6b30 - če pogedaš z (Visual Studio) debuggerjem vidiš, da je vrednost prvega člena tabele res taka
	printf("%d\n", (unsigned long)**tabela); // 78 
	printf("%d\n", (unsigned long)'N'); // 78 
	printf("%#010x\n", (unsigned long)**tabela); // 0x0000004e	
    return 0;
}

marjan_h ::

No, sedaj razumem.

@kow, daj še kakšen primer iz kazalčne aritmetike. Kazalci so najtežji za razumeti, sploh če imamo trojni kazalec, kjer derefenciramo vmes.

kow ::

Kazalcna aritmetika in dereferenciranje sta 2 razlicni stvari.

Ko bos razumel kaj je pointer, bos razumel tudi kaj je kazalcna aritmetika -> ker je samoumevno.
Prvo moras razumeti zakaj rabimo razlicne tipe spremenljivk. To razumes?

marjan_h ::

Pravzaprav nisem še nikoli o tem razmišljal. Zakaj npr. Python ki je šibko tipiziran uspe delovati tako, proti jeziku kjer moraš podati podatkovni tip kot v Cju.

Invictus ::

ZAto pa forsirajo učenje Pythona proti Cju...

Ker te aritmetike nima, oz. je precej dobro skrita.

Kar povzroča cel kup programerjev brez znanja o memory managementu.
"Life is hard; it's even harder when you're stupid."

http://goo.gl/2YuS2x

kow ::

Tip spremenljivke pove koliko celic pomnilnika bo zasedla spremenljivka.
Ce bos imel tabelo integerjev (spremenljivka zasede 4 bajte), bo prevajalnik vedel kje mora prebrati pomnilnik in bo ustvaril pravilno strojno kodo.
Recimo, da hoces zaceti brati od 3. stevilke tabele naprej, bo racunalnik zacel brati 9. pomnilnisko celico oz. offset bo 8.
(prvi int: 0-3, drugi int: 4-7, tretji int: 8-11)

Ker prevajalniku poves tip kazalca (oz. na kateri tip kazalec kaze), ne rabis racunati sam, ampak prevajalnik to naredi zate.

=============================
int* kazalecNaZacetekTabeleIntegerjev;
*(kazalecNaZacetekTabeleIntegerjev + 2) (aritmetika, ki uposteva tip spremenljivke)
=============================

WhiteAngel ::

PS: tud
cout<<
je neke vrste "funkcija".


cout je kar lepo objekt razreda ostream. Slednji ima definirano funkcijo operator<<(), ki sprejme argumente vseh sort podatkovnih tipov.

smacker ::

Drugič preberi do konca, potem pa pametuj.

marjan_h ::

kow je izjavil:

Tip spremenljivke pove koliko celic pomnilnika bo zasedla spremenljivka.
Ce bos imel tabelo integerjev (spremenljivka zasede 4 bajte), bo prevajalnik vedel kje mora prebrati pomnilnik in bo ustvaril pravilno strojno kodo.
Recimo, da hoces zaceti brati od 3. stevilke tabele naprej, bo racunalnik zacel brati 9. pomnilnisko celico oz. offset bo 8.
(prvi int: 0-3, drugi int: 4-7, tretji int: 8-11)

Ker prevajalniku poves tip kazalca (oz. na kateri tip kazalec kaze), ne rabis racunati sam, ampak prevajalnik to naredi zate.

=============================
int* kazalecNaZacetekTabeleIntegerjev;
*(kazalecNaZacetekTabeleIntegerjev + 2) (aritmetika, ki uposteva tip spremenljivke)
=============================


Povej še za python. Kaj se zgodi ko rečem a = 10, nato pa a = "Zvezda". To pomeni, da tolmač razširi te celice pomnilnika?

janig ::

Ne. Ustvari se nov objekt in a zdaj kaze na Zvezda objekt. 10 gre v pozabo.

kow ::

Na Python se ne spoznam kaj prida. Ampak, kot je povedal janig:
logicno se zdi, da v nek nov del pomnilnika interpreter zapise "Zvezda", nato pa prepise tudi vsebino spremenljivke a, ki sedaj kaze na zacetek stringa. "10 gre v pozabo" pomeni, da je v delu pomnilnika na katerega je prej kazala spremenljivka a, se vedno ista vsebina - le nobena spremenljivka vec ne kaze nanj. Ali strokovneje, ni vec referencirana.

janig ::

Ja, v pythonu je vse objekt in spremenljivke so reference na objekte. Vecina jih je tudi immutable - se ne spreminjajo. Recimo tole ustvari dva objekta:
a=1
a=a-1
Zdaj imas v pomnilniku dva objekta 1 in 0. In a kaze na 0. 1 je brez reference.

marjan_h ::

Ok, gremo nazaj na topic. Še ena zanimiva stvar pri kazalcih je t.i "type punning". Kako bi se to prevedlo in kakšen je namen tega? Sem si prebral na wikipedii v angleščini, vendar bi bilo dobro za naslednje generacije, če zna kdo to razložiti?

kow ::

Zgoraj sem pisal, da nekatere stvari dela prevajalnik namesto tebe, da tezje naredis napako.
Tukaj pa ti prevajalniku eksplicitno reces: "vem kaj delam, tretiraj ta del pomnilnika kot ti ukazem".
Primer: "Ceprav je spremenljivka tabela kazalcev na integerje, se pretvarjaj da je spremenljivka tabela kazalcev na znake. Sprejemam odgovornost".

https://stackoverflow.com/questions/251...

blay44 ::

marjan_h je izjavil:



Povej še za python. Kaj se zgodi ko rečem a = 10, nato pa a = "Zvezda". To pomeni, da tolmač razširi te celice pomnilnika?


Kje najdete Python.:D
To je obična skripta, da c++ dobi človeški obraz. Pa čas nam prišpara. Uporaben bo pa takrat, ko boš dobil ven .exe.

V c++(gcc prevajalnik) naredimo nedoločen podatek takole:
#include <iostream>
#include <tuple>
int main ()
{
  std::tuple<int,char> prVa (20,'a');
  auto drUga = std::make_tuple ("To je string", 3.1, 14, 'y', "Basic je zakon");

//prikaz
  std::get<2>(drUga) = 100;
  std::get<0>(prVa) = std::get<2>(drUga);
  std::cout << "Prva vsebuje: ";
  std::cout << std::get<0>(prVa) << ' ';
  std::cout << std::get<1>(prVa) << '\n';
  std::cout << "Druga vsebuje: ";
  std::cout << std::get<0>(drUga) <<' ' ;
  std::cout << std::get<2>(drUga) <<'\n';
  std::cout << std::get<4>(drUga) <<'\n';
  return 0;
}

V VS.net je pa še bolj enostavno. Samo rabiš licenco.

Zgodovina sprememb…

  • spremenil: blay44 ()


Vredno ogleda ...

TemaSporočilaOglediZadnje sporočilo
TemaSporočilaOglediZadnje sporočilo
»

[Java] array v voidu

Oddelek: Programiranje
102076 (1775) Spura
»

[C] struct in int[] (strani: 1 2 )

Oddelek: Programiranje
656743 (5816) MrBrdo
»

C osnova

Oddelek: Programiranje
221467 (982) RunoTheDog
»

[C] Narascajoce sortiranje linearnega seznama

Oddelek: Programiranje
71756 (1645) Jebiveter
»

kazalci in polje

Oddelek: Programiranje
51595 (1516) rasta

Več podobnih tem