What Is Difference Between "P" and "Pp"

What is difference between p and pp?

p is used to inspect a variable as a debug aide. It works printing the output of the method #inspect. For example p foo will output the content of foo.inspect.

Sometimes you need to debug complex variables or nested variables. In this case p will output a long line that is hard to understand. Instead, pp will put try to arrange the content of the variable so that it is easier to understand, for example indenting nested arrays or using one line for each instance variable of a complex object. pp does this calling the #pretty_inspect method (the pp library adds #pretty_inspect methods to many classes such as String, Array or Struct).

To remember: p = print, pp = pretty print.

What's the difference between `auto pp` and `auto *ppp`?

In the particular example you show, there is no difference. But imagine you would later on add two const qualifier like the following:

const auto pp = p;
const auto *ppp = p;

Is it still the same? Turns out that this is identical to

int * const pp = p; // pointer is readonly
const int *ppp = p; // pointer is readonly

because in auto pp = p, auto matches int* as a whole, and const modifies what's on its left (or what's on its right, if there is nothing on its left). Contrary, in auto *ppp = p, auto matches int, and this is what const applies to.

Because of this notable difference and because we should use const variables whenever possible, I'd advise you to always use auto* when using type deduction for pointer variables. There is no way to const-qualify the pointer itself instead of the pointee, and if you want to const-qualify both, this is possible by

const auto * const pppp = p;

which doesn't work without the *.

C++ - *p vs &p vs p

The * operator is used for indirection. Indirection means the value in p is interpreted as a memory address and the value at that address is loaded. p is the value of p while *p is the value stored in the memory location pointed by p. When you want to indirectly access the value of an integer i, you can have an integer pointer point to it (int *p = &i) and use that pointer to modify the value of i indirectly (*p = 10).

double pointer output explanation

When you use the name of an array (in most contexts), it decays to a pointer to its first element. That means that int* p = a; and int* p = &a[0]; are exactly the same.

So to understand what happens in this case, just walk through step by step. At the point of your first printf call, things look like this:

 pp            p           a
+-------+ +------+ +----+----+----+----+
| +---------> +--------> -1 | -2 | -3 | -4 |
+-------+ | | +----+----+----+----+
| |
+------+ b
| | +----+----+----+----+
| +---------> 0 | 1 | 2 | 3 |
| | +----+----+----+----+
+------+

pp points to the first element of p, which is a pointer to the first element of a.

Now, when you increment pp, it changes to point to the second element of p, which is a pointer to the first element of b:

 pp            p           a
+-------+ +------+ +----+----+----+----+
| + | | +--------> -1 | -2 | -3 | -4 |
+---|---+ | | +----+----+----+----+
| | |
| +------+ b
| | | +----+----+----+----+
+---------> +---------> 0 | 1 | 2 | 3 |
| | +----+----+----+----+
+------+

You then increment *pp. Since *pp is a pointer to the first element of b, that pointer is incremented to point to the second element of b:

 pp            p           a
+-------+ +------+ +----+----+----+----+
| + | | +--------> -1 | -2 | -3 | -4 |
+---|---+ | | +----+----+----+----+
| | |
| +------+ b
| | | +----+----+----+----+
+---------> | | 0 | 1 | 2 | 3 |
| + | +----+-^--+----+----+
+---|--+ |
+---------------+

Then you increment **pp. At this point pp is a pointer to the second element of p, so *pp is a pointer to the second element of b. That means **pp names the second element of b. You increment that from 1 to 2:

 pp            p           a
+-------+ +------+ +----+----+----+----+
| + | | +--------> -1 | -2 | -3 | -4 |
+---|---+ | | +----+----+----+----+
| | |
| +------+ b
| | | +----+----+----+----+
+---------> | | 0 | 2 | 2 | 3 |
| + | +----+-^--+----+----+
+---|--+ |
+---------------+

Now, lets dissect (++**pp)[a]. ++**pp is the same as before, so the second element of b gets incremented to 3.

Now, for any pointer ptr and integer n, ptr[n] is the same as *(ptr + n). Since addition is commutative, ptr + n is the same as n + ptr. That means ptr[n] is the same as n[ptr].

Putting these together, that means that (++**pp)[a] is the same as 3[a], which is the same as a[3]. a[3] is -4, hence your result.

What is the difference between cpu/mem-loads/pp and cpu/mem-loads/?

The p modifier stands for precise level When doing sampling, it used to indicate the skid you tolerate: how far can be t reported instruction from the effective instructions that generated the sample. pp means that the SAMPLE_IP is requested to have 0 skid. In other word, when you do memory accesses sampling, you want to know exactly which instruction generated the access.

See man perf list:

p - precise level
....
The p modifier can be used for specifying how precise the instruction address should be. The p modifier can be specified multiple times:

0 - SAMPLE_IP can have arbitrary skid
1 - SAMPLE_IP must have constant skid
2 - SAMPLE_IP requested to have 0 skid
3 - SAMPLE_IP must have 0 skid

For Intel systems precise event sampling is implemented with PEBS which supports up to precise-level 2.

On AMD systems it is implemented using IBS (up to precise-level 2). The precise modifier works with event types 0x76 (cpu-cycles, CPU clocks not halted) and 0xC1 (micro-ops
retired). Both events map to IBS execution sampling (IBS op) with the IBS Op Counter Control bit (IbsOpCntCtl) set respectively (see AMD64 Architecture Programmer’s Manual Volume
2: System Programming, 13.3 Instruction-Based Sampling). Examples to use IBS:

perf record -a -e cpu-cycles:p ... # use ibs op counting cycles
perf record -a -e r076:p ... # same as -e cpu-cycles:p
perf record -a -e r0C1:p ... # use ibs op counting micro-ops

Dereference pointer *p vs (%s, p)

A pointer doesn't necessarily contain address of another variable, it contains an address of some data, which can but doesn't need to be in a variable. For example, the pointer may point to dynamically allocated data, or to data that is part of an array, or to static data that ships with the executable.

In case of char *p = "hello", p points to the first character of a character array prepared by the compiler, and which contains the letters hello followed by a terminating zero character. The %s specifier is designed to receive such a pointer and print the letters up to the zero character, which is why it works.

Your second example, where you print a pointer using %d is quite different in nature. It abuses the fact that addresses have integer representations, and that T *x = <some integer> implicitly casts the integer to the corresponding memory address. In your case you create an invalid pointer pointing to the invalid address 10. Then you proceed to call printf with the %d specifier without dereferencing the pointer. Since %d expects an integer, and the address is also internally represented as an integer, this prints the invalid address 10 the pointer points to. However, pointers and integers don't have compatible memory representations, so this kind of code only works by accident, and exhibits undefined behavior.



Related Topics



Leave a reply



Submit